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 self,
  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 self, e: "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 self, e: "EXTERNAL",
  94        exp.HeapProperty: lambda self, e: "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 self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 108        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
 109        exp.NonClusteredColumnConstraint: lambda self,
 110        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 111        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
 112        exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
 113        exp.OnCommitProperty: lambda self,
 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 self, 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 self, e: e.name,
 127        exp.SqlSecurityProperty: lambda self,
 128        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 129        exp.StabilityProperty: lambda self, e: e.name,
 130        exp.TemporaryProperty: lambda self, e: "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 self, e: "TRANSIENT",
 136        exp.UppercaseColumnConstraint: lambda self, e: "UPPERCASE",
 137        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 138        exp.VolatileProperty: lambda self, e: "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.LockingProperty: exp.Properties.Location.POST_ALIAS,
 392        exp.LogProperty: exp.Properties.Location.POST_NAME,
 393        exp.MaterializedProperty: exp.Properties.Location.POST_CREATE,
 394        exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME,
 395        exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION,
 396        exp.OnProperty: exp.Properties.Location.POST_SCHEMA,
 397        exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION,
 398        exp.Order: exp.Properties.Location.POST_SCHEMA,
 399        exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA,
 400        exp.PartitionedByProperty: exp.Properties.Location.POST_WITH,
 401        exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA,
 402        exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA,
 403        exp.Property: exp.Properties.Location.POST_WITH,
 404        exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA,
 405        exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA,
 406        exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA,
 407        exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA,
 408        exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA,
 409        exp.SampleProperty: exp.Properties.Location.POST_SCHEMA,
 410        exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA,
 411        exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA,
 412        exp.Set: exp.Properties.Location.POST_SCHEMA,
 413        exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA,
 414        exp.SetProperty: exp.Properties.Location.POST_CREATE,
 415        exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA,
 416        exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA,
 417        exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA,
 418        exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE,
 419        exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA,
 420        exp.TemporaryProperty: exp.Properties.Location.POST_CREATE,
 421        exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA,
 422        exp.TransientProperty: exp.Properties.Location.POST_CREATE,
 423        exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA,
 424        exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA,
 425        exp.VolatileProperty: exp.Properties.Location.POST_CREATE,
 426        exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION,
 427        exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME,
 428        exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA,
 429    }
 430
 431    # Keywords that can't be used as unquoted identifier names
 432    RESERVED_KEYWORDS: t.Set[str] = set()
 433
 434    # Expressions whose comments are separated from them for better formatting
 435    WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 436        exp.Create,
 437        exp.Delete,
 438        exp.Drop,
 439        exp.From,
 440        exp.Insert,
 441        exp.Join,
 442        exp.Select,
 443        exp.Update,
 444        exp.Where,
 445        exp.With,
 446    )
 447
 448    # Expressions that should not have their comments generated in maybe_comment
 449    EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
 450        exp.Binary,
 451        exp.Union,
 452    )
 453
 454    # Expressions that can remain unwrapped when appearing in the context of an INTERVAL
 455    UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = (
 456        exp.Column,
 457        exp.Literal,
 458        exp.Neg,
 459        exp.Paren,
 460    )
 461
 462    # Expressions that need to have all CTEs under them bubbled up to them
 463    EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 464
 465    KEY_VALUE_DEFINITIONS = (exp.EQ, exp.PropertyEQ, exp.Slice)
 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.Select, exp.Union))
 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_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1539        table = ".".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        version = self.sql(expression, "version")
1550        version = f" {version}" if version else ""
1551        alias = self.sql(expression, "alias")
1552        alias = f"{sep}{alias}" if alias else ""
1553        hints = self.expressions(expression, key="hints", sep=" ")
1554        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1555        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1556        pivots = f" {pivots}" if pivots else ""
1557        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1558        laterals = self.expressions(expression, key="laterals", sep="")
1559
1560        file_format = self.sql(expression, "format")
1561        if file_format:
1562            pattern = self.sql(expression, "pattern")
1563            pattern = f", PATTERN => {pattern}" if pattern else ""
1564            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1565
1566        ordinality = expression.args.get("ordinality") or ""
1567        if ordinality:
1568            ordinality = f" WITH ORDINALITY{alias}"
1569            alias = ""
1570
1571        when = self.sql(expression, "when")
1572        if when:
1573            table = f"{table} {when}"
1574
1575        return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
1576
1577    def tablesample_sql(
1578        self,
1579        expression: exp.TableSample,
1580        sep: str = " AS ",
1581        tablesample_keyword: t.Optional[str] = None,
1582    ) -> str:
1583        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1584            table = expression.this.copy()
1585            table.set("alias", None)
1586            this = self.sql(table)
1587            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1588        else:
1589            this = self.sql(expression, "this")
1590            alias = ""
1591
1592        method = self.sql(expression, "method")
1593        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1594        numerator = self.sql(expression, "bucket_numerator")
1595        denominator = self.sql(expression, "bucket_denominator")
1596        field = self.sql(expression, "bucket_field")
1597        field = f" ON {field}" if field else ""
1598        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1599        seed = self.sql(expression, "seed")
1600        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1601
1602        size = self.sql(expression, "size")
1603        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1604            size = f"{size} ROWS"
1605
1606        percent = self.sql(expression, "percent")
1607        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1608            percent = f"{percent} PERCENT"
1609
1610        expr = f"{bucket}{percent}{size}"
1611        if self.TABLESAMPLE_REQUIRES_PARENS:
1612            expr = f"({expr})"
1613
1614        return (
1615            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1616        )
1617
1618    def pivot_sql(self, expression: exp.Pivot) -> str:
1619        expressions = self.expressions(expression, flat=True)
1620
1621        if expression.this:
1622            this = self.sql(expression, "this")
1623            if not expressions:
1624                return f"UNPIVOT {this}"
1625
1626            on = f"{self.seg('ON')} {expressions}"
1627            using = self.expressions(expression, key="using", flat=True)
1628            using = f"{self.seg('USING')} {using}" if using else ""
1629            group = self.sql(expression, "group")
1630            return f"PIVOT {this}{on}{using}{group}"
1631
1632        alias = self.sql(expression, "alias")
1633        alias = f" AS {alias}" if alias else ""
1634        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1635        field = self.sql(expression, "field")
1636        include_nulls = expression.args.get("include_nulls")
1637        if include_nulls is not None:
1638            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1639        else:
1640            nulls = ""
1641        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1642
1643    def version_sql(self, expression: exp.Version) -> str:
1644        this = f"FOR {expression.name}"
1645        kind = expression.text("kind")
1646        expr = self.sql(expression, "expression")
1647        return f"{this} {kind} {expr}"
1648
1649    def tuple_sql(self, expression: exp.Tuple) -> str:
1650        return f"({self.expressions(expression, flat=True)})"
1651
1652    def update_sql(self, expression: exp.Update) -> str:
1653        this = self.sql(expression, "this")
1654        set_sql = self.expressions(expression, flat=True)
1655        from_sql = self.sql(expression, "from")
1656        where_sql = self.sql(expression, "where")
1657        returning = self.sql(expression, "returning")
1658        order = self.sql(expression, "order")
1659        limit = self.sql(expression, "limit")
1660        if self.RETURNING_END:
1661            expression_sql = f"{from_sql}{where_sql}{returning}"
1662        else:
1663            expression_sql = f"{returning}{from_sql}{where_sql}"
1664        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1665        return self.prepend_ctes(expression, sql)
1666
1667    def values_sql(self, expression: exp.Values) -> str:
1668        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1669        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1670            args = self.expressions(expression)
1671            alias = self.sql(expression, "alias")
1672            values = f"VALUES{self.seg('')}{args}"
1673            values = (
1674                f"({values})"
1675                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1676                else values
1677            )
1678            return f"{values} AS {alias}" if alias else values
1679
1680        # Converts `VALUES...` expression into a series of select unions.
1681        alias_node = expression.args.get("alias")
1682        column_names = alias_node and alias_node.columns
1683
1684        selects: t.List[exp.Subqueryable] = []
1685
1686        for i, tup in enumerate(expression.expressions):
1687            row = tup.expressions
1688
1689            if i == 0 and column_names:
1690                row = [
1691                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1692                ]
1693
1694            selects.append(exp.Select(expressions=row))
1695
1696        if self.pretty:
1697            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1698            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1699            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1700            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1701            return self.subquery_sql(
1702                subqueryable.subquery(alias_node and alias_node.this, copy=False)
1703            )
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        return csv(
2074            *sqls,
2075            *[self.sql(join) for join in expression.args.get("joins") or []],
2076            self.sql(expression, "connect"),
2077            self.sql(expression, "match"),
2078            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2079            self.sql(expression, "where"),
2080            self.sql(expression, "group"),
2081            self.sql(expression, "having"),
2082            *self.after_having_modifiers(expression),
2083            self.sql(expression, "order"),
2084            *offset_limit_modifiers,
2085            *self.after_limit_modifiers(expression),
2086            sep="",
2087        )
2088
2089    def offset_limit_modifiers(
2090        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2091    ) -> t.List[str]:
2092        return [
2093            self.sql(expression, "offset") if fetch else self.sql(limit),
2094            self.sql(limit) if fetch else self.sql(expression, "offset"),
2095        ]
2096
2097    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
2098        return [
2099            self.sql(expression, "qualify"),
2100            (
2101                self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
2102                if expression.args.get("windows")
2103                else ""
2104            ),
2105            self.sql(expression, "distribute"),
2106            self.sql(expression, "sort"),
2107            self.sql(expression, "cluster"),
2108        ]
2109
2110    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2111        locks = self.expressions(expression, key="locks", sep=" ")
2112        locks = f" {locks}" if locks else ""
2113        return [locks, self.sql(expression, "sample")]
2114
2115    def select_sql(self, expression: exp.Select) -> str:
2116        into = expression.args.get("into")
2117        if not self.SUPPORTS_SELECT_INTO and into:
2118            into.pop()
2119
2120        hint = self.sql(expression, "hint")
2121        distinct = self.sql(expression, "distinct")
2122        distinct = f" {distinct}" if distinct else ""
2123        kind = self.sql(expression, "kind")
2124        limit = expression.args.get("limit")
2125        top = (
2126            self.limit_sql(limit, top=True)
2127            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2128            else ""
2129        )
2130
2131        expressions = self.expressions(expression)
2132
2133        if kind:
2134            if kind in self.SELECT_KINDS:
2135                kind = f" AS {kind}"
2136            else:
2137                if kind == "STRUCT":
2138                    expressions = self.expressions(
2139                        sqls=[
2140                            self.sql(
2141                                exp.Struct(
2142                                    expressions=[
2143                                        exp.column(e.output_name).eq(
2144                                            e.this if isinstance(e, exp.Alias) else e
2145                                        )
2146                                        for e in expression.expressions
2147                                    ]
2148                                )
2149                            )
2150                        ]
2151                    )
2152                kind = ""
2153
2154        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2155        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2156        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2157        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2158        sql = self.query_modifiers(
2159            expression,
2160            f"SELECT{top_distinct}{kind}{expressions}",
2161            self.sql(expression, "into", comment=False),
2162            self.sql(expression, "from", comment=False),
2163        )
2164
2165        sql = self.prepend_ctes(expression, sql)
2166
2167        if not self.SUPPORTS_SELECT_INTO and into:
2168            if into.args.get("temporary"):
2169                table_kind = " TEMPORARY"
2170            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2171                table_kind = " UNLOGGED"
2172            else:
2173                table_kind = ""
2174            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2175
2176        return sql
2177
2178    def schema_sql(self, expression: exp.Schema) -> str:
2179        this = self.sql(expression, "this")
2180        sql = self.schema_columns_sql(expression)
2181        return f"{this} {sql}" if this and sql else this or sql
2182
2183    def schema_columns_sql(self, expression: exp.Schema) -> str:
2184        if expression.expressions:
2185            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2186        return ""
2187
2188    def star_sql(self, expression: exp.Star) -> str:
2189        except_ = self.expressions(expression, key="except", flat=True)
2190        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2191        replace = self.expressions(expression, key="replace", flat=True)
2192        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2193        return f"*{except_}{replace}"
2194
2195    def parameter_sql(self, expression: exp.Parameter) -> str:
2196        this = self.sql(expression, "this")
2197        return f"{self.PARAMETER_TOKEN}{this}"
2198
2199    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2200        this = self.sql(expression, "this")
2201        kind = expression.text("kind")
2202        if kind:
2203            kind = f"{kind}."
2204        return f"@@{kind}{this}"
2205
2206    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2207        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
2208
2209    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2210        alias = self.sql(expression, "alias")
2211        alias = f"{sep}{alias}" if alias else ""
2212
2213        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2214        pivots = f" {pivots}" if pivots else ""
2215
2216        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2217        return self.prepend_ctes(expression, sql)
2218
2219    def qualify_sql(self, expression: exp.Qualify) -> str:
2220        this = self.indent(self.sql(expression, "this"))
2221        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2222
2223    def union_sql(self, expression: exp.Union) -> str:
2224        return self.prepend_ctes(
2225            expression,
2226            self.set_operation(expression, self.union_op(expression)),
2227        )
2228
2229    def union_op(self, expression: exp.Union) -> str:
2230        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2231        kind = kind if expression.args.get("distinct") else " ALL"
2232        by_name = " BY NAME" if expression.args.get("by_name") else ""
2233        return f"UNION{kind}{by_name}"
2234
2235    def unnest_sql(self, expression: exp.Unnest) -> str:
2236        args = self.expressions(expression, flat=True)
2237
2238        alias = expression.args.get("alias")
2239        offset = expression.args.get("offset")
2240
2241        if self.UNNEST_WITH_ORDINALITY:
2242            if alias and isinstance(offset, exp.Expression):
2243                alias.append("columns", offset)
2244
2245        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2246            columns = alias.columns
2247            alias = self.sql(columns[0]) if columns else ""
2248        else:
2249            alias = self.sql(alias)
2250
2251        alias = f" AS {alias}" if alias else alias
2252        if self.UNNEST_WITH_ORDINALITY:
2253            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2254        else:
2255            if isinstance(offset, exp.Expression):
2256                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2257            elif offset:
2258                suffix = f"{alias} WITH OFFSET"
2259            else:
2260                suffix = alias
2261
2262        return f"UNNEST({args}){suffix}"
2263
2264    def where_sql(self, expression: exp.Where) -> str:
2265        this = self.indent(self.sql(expression, "this"))
2266        return f"{self.seg('WHERE')}{self.sep()}{this}"
2267
2268    def window_sql(self, expression: exp.Window) -> str:
2269        this = self.sql(expression, "this")
2270        partition = self.partition_by_sql(expression)
2271        order = expression.args.get("order")
2272        order = self.order_sql(order, flat=True) if order else ""
2273        spec = self.sql(expression, "spec")
2274        alias = self.sql(expression, "alias")
2275        over = self.sql(expression, "over") or "OVER"
2276
2277        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2278
2279        first = expression.args.get("first")
2280        if first is None:
2281            first = ""
2282        else:
2283            first = "FIRST" if first else "LAST"
2284
2285        if not partition and not order and not spec and alias:
2286            return f"{this} {alias}"
2287
2288        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2289        return f"{this} ({args})"
2290
2291    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2292        partition = self.expressions(expression, key="partition_by", flat=True)
2293        return f"PARTITION BY {partition}" if partition else ""
2294
2295    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2296        kind = self.sql(expression, "kind")
2297        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2298        end = (
2299            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2300            or "CURRENT ROW"
2301        )
2302        return f"{kind} BETWEEN {start} AND {end}"
2303
2304    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2305        this = self.sql(expression, "this")
2306        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2307        return f"{this} WITHIN GROUP ({expression_sql})"
2308
2309    def between_sql(self, expression: exp.Between) -> str:
2310        this = self.sql(expression, "this")
2311        low = self.sql(expression, "low")
2312        high = self.sql(expression, "high")
2313        return f"{this} BETWEEN {low} AND {high}"
2314
2315    def bracket_sql(self, expression: exp.Bracket) -> str:
2316        expressions = apply_index_offset(
2317            expression.this,
2318            expression.expressions,
2319            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2320        )
2321        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2322        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2323
2324    def all_sql(self, expression: exp.All) -> str:
2325        return f"ALL {self.wrap(expression)}"
2326
2327    def any_sql(self, expression: exp.Any) -> str:
2328        this = self.sql(expression, "this")
2329        if isinstance(expression.this, exp.Subqueryable):
2330            this = self.wrap(this)
2331        return f"ANY {this}"
2332
2333    def exists_sql(self, expression: exp.Exists) -> str:
2334        return f"EXISTS{self.wrap(expression)}"
2335
2336    def case_sql(self, expression: exp.Case) -> str:
2337        this = self.sql(expression, "this")
2338        statements = [f"CASE {this}" if this else "CASE"]
2339
2340        for e in expression.args["ifs"]:
2341            statements.append(f"WHEN {self.sql(e, 'this')}")
2342            statements.append(f"THEN {self.sql(e, 'true')}")
2343
2344        default = self.sql(expression, "default")
2345
2346        if default:
2347            statements.append(f"ELSE {default}")
2348
2349        statements.append("END")
2350
2351        if self.pretty and self.text_width(statements) > self.max_text_width:
2352            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2353
2354        return " ".join(statements)
2355
2356    def constraint_sql(self, expression: exp.Constraint) -> str:
2357        this = self.sql(expression, "this")
2358        expressions = self.expressions(expression, flat=True)
2359        return f"CONSTRAINT {this} {expressions}"
2360
2361    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2362        order = expression.args.get("order")
2363        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2364        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2365
2366    def extract_sql(self, expression: exp.Extract) -> str:
2367        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2368        expression_sql = self.sql(expression, "expression")
2369        return f"EXTRACT({this} FROM {expression_sql})"
2370
2371    def trim_sql(self, expression: exp.Trim) -> str:
2372        trim_type = self.sql(expression, "position")
2373
2374        if trim_type == "LEADING":
2375            return self.func("LTRIM", expression.this)
2376        elif trim_type == "TRAILING":
2377            return self.func("RTRIM", expression.this)
2378        else:
2379            return self.func("TRIM", expression.this, expression.expression)
2380
2381    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2382        args = expression.expressions
2383        if isinstance(expression, exp.ConcatWs):
2384            args = args[1:]  # Skip the delimiter
2385
2386        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2387            args = [exp.cast(e, "text") for e in args]
2388
2389        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2390            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2391
2392        return args
2393
2394    def concat_sql(self, expression: exp.Concat) -> str:
2395        expressions = self.convert_concat_args(expression)
2396
2397        # Some dialects don't allow a single-argument CONCAT call
2398        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2399            return self.sql(expressions[0])
2400
2401        return self.func("CONCAT", *expressions)
2402
2403    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2404        return self.func(
2405            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2406        )
2407
2408    def check_sql(self, expression: exp.Check) -> str:
2409        this = self.sql(expression, key="this")
2410        return f"CHECK ({this})"
2411
2412    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2413        expressions = self.expressions(expression, flat=True)
2414        reference = self.sql(expression, "reference")
2415        reference = f" {reference}" if reference else ""
2416        delete = self.sql(expression, "delete")
2417        delete = f" ON DELETE {delete}" if delete else ""
2418        update = self.sql(expression, "update")
2419        update = f" ON UPDATE {update}" if update else ""
2420        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2421
2422    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2423        expressions = self.expressions(expression, flat=True)
2424        options = self.expressions(expression, key="options", flat=True, sep=" ")
2425        options = f" {options}" if options else ""
2426        return f"PRIMARY KEY ({expressions}){options}"
2427
2428    def if_sql(self, expression: exp.If) -> str:
2429        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2430
2431    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2432        modifier = expression.args.get("modifier")
2433        modifier = f" {modifier}" if modifier else ""
2434        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2435
2436    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2437        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2438
2439    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2440        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2441        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2442
2443    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2444        if isinstance(expression, exp.JSONPathPart):
2445            transform = self.TRANSFORMS.get(expression.__class__)
2446            if not callable(transform):
2447                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2448                return ""
2449
2450            return transform(self, expression)
2451
2452        if isinstance(expression, int):
2453            return str(expression)
2454
2455        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2456            escaped = expression.replace("'", "\\'")
2457            escaped = f"\\'{expression}\\'"
2458        else:
2459            escaped = expression.replace('"', '\\"')
2460            escaped = f'"{escaped}"'
2461
2462        return escaped
2463
2464    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2465        return f"{self.sql(expression, 'this')} FORMAT JSON"
2466
2467    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2468        null_handling = expression.args.get("null_handling")
2469        null_handling = f" {null_handling}" if null_handling else ""
2470
2471        unique_keys = expression.args.get("unique_keys")
2472        if unique_keys is not None:
2473            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2474        else:
2475            unique_keys = ""
2476
2477        return_type = self.sql(expression, "return_type")
2478        return_type = f" RETURNING {return_type}" if return_type else ""
2479        encoding = self.sql(expression, "encoding")
2480        encoding = f" ENCODING {encoding}" if encoding else ""
2481
2482        return self.func(
2483            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2484            *expression.expressions,
2485            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2486        )
2487
2488    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2489        return self.jsonobject_sql(expression)
2490
2491    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2492        null_handling = expression.args.get("null_handling")
2493        null_handling = f" {null_handling}" if null_handling else ""
2494        return_type = self.sql(expression, "return_type")
2495        return_type = f" RETURNING {return_type}" if return_type else ""
2496        strict = " STRICT" if expression.args.get("strict") else ""
2497        return self.func(
2498            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2499        )
2500
2501    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2502        this = self.sql(expression, "this")
2503        order = self.sql(expression, "order")
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_ARRAYAGG",
2511            this,
2512            suffix=f"{order}{null_handling}{return_type}{strict})",
2513        )
2514
2515    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2516        path = self.sql(expression, "path")
2517        path = f" PATH {path}" if path else ""
2518        nested_schema = self.sql(expression, "nested_schema")
2519
2520        if nested_schema:
2521            return f"NESTED{path} {nested_schema}"
2522
2523        this = self.sql(expression, "this")
2524        kind = self.sql(expression, "kind")
2525        kind = f" {kind}" if kind else ""
2526        return f"{this}{kind}{path}"
2527
2528    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2529        return self.func("COLUMNS", *expression.expressions)
2530
2531    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2532        this = self.sql(expression, "this")
2533        path = self.sql(expression, "path")
2534        path = f", {path}" if path else ""
2535        error_handling = expression.args.get("error_handling")
2536        error_handling = f" {error_handling}" if error_handling else ""
2537        empty_handling = expression.args.get("empty_handling")
2538        empty_handling = f" {empty_handling}" if empty_handling else ""
2539        schema = self.sql(expression, "schema")
2540        return self.func(
2541            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2542        )
2543
2544    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2545        this = self.sql(expression, "this")
2546        kind = self.sql(expression, "kind")
2547        path = self.sql(expression, "path")
2548        path = f" {path}" if path else ""
2549        as_json = " AS JSON" if expression.args.get("as_json") else ""
2550        return f"{this} {kind}{path}{as_json}"
2551
2552    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2553        this = self.sql(expression, "this")
2554        path = self.sql(expression, "path")
2555        path = f", {path}" if path else ""
2556        expressions = self.expressions(expression)
2557        with_ = (
2558            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2559            if expressions
2560            else ""
2561        )
2562        return f"OPENJSON({this}{path}){with_}"
2563
2564    def in_sql(self, expression: exp.In) -> str:
2565        query = expression.args.get("query")
2566        unnest = expression.args.get("unnest")
2567        field = expression.args.get("field")
2568        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2569
2570        if query:
2571            in_sql = self.wrap(query)
2572        elif unnest:
2573            in_sql = self.in_unnest_op(unnest)
2574        elif field:
2575            in_sql = self.sql(field)
2576        else:
2577            in_sql = f"({self.expressions(expression, flat=True)})"
2578
2579        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2580
2581    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2582        return f"(SELECT {self.sql(unnest)})"
2583
2584    def interval_sql(self, expression: exp.Interval) -> str:
2585        unit = self.sql(expression, "unit")
2586        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2587            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2588        unit = f" {unit}" if unit else ""
2589
2590        if self.SINGLE_STRING_INTERVAL:
2591            this = expression.this.name if expression.this else ""
2592            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2593
2594        this = self.sql(expression, "this")
2595        if this:
2596            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2597            this = f" {this}" if unwrapped else f" ({this})"
2598
2599        return f"INTERVAL{this}{unit}"
2600
2601    def return_sql(self, expression: exp.Return) -> str:
2602        return f"RETURN {self.sql(expression, 'this')}"
2603
2604    def reference_sql(self, expression: exp.Reference) -> str:
2605        this = self.sql(expression, "this")
2606        expressions = self.expressions(expression, flat=True)
2607        expressions = f"({expressions})" if expressions else ""
2608        options = self.expressions(expression, key="options", flat=True, sep=" ")
2609        options = f" {options}" if options else ""
2610        return f"REFERENCES {this}{expressions}{options}"
2611
2612    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2613        return self.func(expression.name, *expression.expressions)
2614
2615    def paren_sql(self, expression: exp.Paren) -> str:
2616        if isinstance(expression.unnest(), exp.Select):
2617            sql = self.wrap(expression)
2618        else:
2619            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2620            sql = f"({sql}{self.seg(')', sep='')}"
2621
2622        return self.prepend_ctes(expression, sql)
2623
2624    def neg_sql(self, expression: exp.Neg) -> str:
2625        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2626        this_sql = self.sql(expression, "this")
2627        sep = " " if this_sql[0] == "-" else ""
2628        return f"-{sep}{this_sql}"
2629
2630    def not_sql(self, expression: exp.Not) -> str:
2631        return f"NOT {self.sql(expression, 'this')}"
2632
2633    def alias_sql(self, expression: exp.Alias) -> str:
2634        alias = self.sql(expression, "alias")
2635        alias = f" AS {alias}" if alias else ""
2636        return f"{self.sql(expression, 'this')}{alias}"
2637
2638    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2639        alias = expression.args["alias"]
2640        identifier_alias = isinstance(alias, exp.Identifier)
2641
2642        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2643            alias.replace(exp.Literal.string(alias.output_name))
2644        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2645            alias.replace(exp.to_identifier(alias.output_name))
2646
2647        return self.alias_sql(expression)
2648
2649    def aliases_sql(self, expression: exp.Aliases) -> str:
2650        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
2651
2652    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2653        this = self.sql(expression, "this")
2654        index = self.sql(expression, "expression")
2655        return f"{this} AT {index}"
2656
2657    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2658        this = self.sql(expression, "this")
2659        zone = self.sql(expression, "zone")
2660        return f"{this} AT TIME ZONE {zone}"
2661
2662    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2663        this = self.sql(expression, "this")
2664        zone = self.sql(expression, "zone")
2665        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
2666
2667    def add_sql(self, expression: exp.Add) -> str:
2668        return self.binary(expression, "+")
2669
2670    def and_sql(self, expression: exp.And) -> str:
2671        return self.connector_sql(expression, "AND")
2672
2673    def xor_sql(self, expression: exp.Xor) -> str:
2674        return self.connector_sql(expression, "XOR")
2675
2676    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2677        if not self.pretty:
2678            return self.binary(expression, op)
2679
2680        sqls = tuple(
2681            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2682            for i, e in enumerate(expression.flatten(unnest=False))
2683        )
2684
2685        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2686        return f"{sep}{op} ".join(sqls)
2687
2688    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2689        return self.binary(expression, "&")
2690
2691    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2692        return self.binary(expression, "<<")
2693
2694    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2695        return f"~{self.sql(expression, 'this')}"
2696
2697    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2698        return self.binary(expression, "|")
2699
2700    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2701        return self.binary(expression, ">>")
2702
2703    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2704        return self.binary(expression, "^")
2705
2706    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2707        format_sql = self.sql(expression, "format")
2708        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2709        to_sql = self.sql(expression, "to")
2710        to_sql = f" {to_sql}" if to_sql else ""
2711        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2712
2713    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2714        zone = self.sql(expression, "this")
2715        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
2716
2717    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2718        return self.func("CURRENT_TIMESTAMP", expression.this)
2719
2720    def collate_sql(self, expression: exp.Collate) -> str:
2721        if self.COLLATE_IS_FUNC:
2722            return self.function_fallback_sql(expression)
2723        return self.binary(expression, "COLLATE")
2724
2725    def command_sql(self, expression: exp.Command) -> str:
2726        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
2727
2728    def comment_sql(self, expression: exp.Comment) -> str:
2729        this = self.sql(expression, "this")
2730        kind = expression.args["kind"]
2731        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2732        expression_sql = self.sql(expression, "expression")
2733        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2734
2735    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2736        this = self.sql(expression, "this")
2737        delete = " DELETE" if expression.args.get("delete") else ""
2738        recompress = self.sql(expression, "recompress")
2739        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2740        to_disk = self.sql(expression, "to_disk")
2741        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2742        to_volume = self.sql(expression, "to_volume")
2743        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2744        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2745
2746    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2747        where = self.sql(expression, "where")
2748        group = self.sql(expression, "group")
2749        aggregates = self.expressions(expression, key="aggregates")
2750        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2751
2752        if not (where or group or aggregates) and len(expression.expressions) == 1:
2753            return f"TTL {self.expressions(expression, flat=True)}"
2754
2755        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2756
2757    def transaction_sql(self, expression: exp.Transaction) -> str:
2758        return "BEGIN"
2759
2760    def commit_sql(self, expression: exp.Commit) -> str:
2761        chain = expression.args.get("chain")
2762        if chain is not None:
2763            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2764
2765        return f"COMMIT{chain or ''}"
2766
2767    def rollback_sql(self, expression: exp.Rollback) -> str:
2768        savepoint = expression.args.get("savepoint")
2769        savepoint = f" TO {savepoint}" if savepoint else ""
2770        return f"ROLLBACK{savepoint}"
2771
2772    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2773        this = self.sql(expression, "this")
2774
2775        dtype = self.sql(expression, "dtype")
2776        if dtype:
2777            collate = self.sql(expression, "collate")
2778            collate = f" COLLATE {collate}" if collate else ""
2779            using = self.sql(expression, "using")
2780            using = f" USING {using}" if using else ""
2781            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2782
2783        default = self.sql(expression, "default")
2784        if default:
2785            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2786
2787        comment = self.sql(expression, "comment")
2788        if comment:
2789            return f"ALTER COLUMN {this} COMMENT {comment}"
2790
2791        if not expression.args.get("drop"):
2792            self.unsupported("Unsupported ALTER COLUMN syntax")
2793
2794        return f"ALTER COLUMN {this} DROP DEFAULT"
2795
2796    def renametable_sql(self, expression: exp.RenameTable) -> str:
2797        if not self.RENAME_TABLE_WITH_DB:
2798            # Remove db from tables
2799            expression = expression.transform(
2800                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2801            )
2802        this = self.sql(expression, "this")
2803        return f"RENAME TO {this}"
2804
2805    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2806        exists = " IF EXISTS" if expression.args.get("exists") else ""
2807        old_column = self.sql(expression, "this")
2808        new_column = self.sql(expression, "to")
2809        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
2810
2811    def altertable_sql(self, expression: exp.AlterTable) -> str:
2812        actions = expression.args["actions"]
2813
2814        if isinstance(actions[0], exp.ColumnDef):
2815            actions = self.add_column_sql(expression)
2816        elif isinstance(actions[0], exp.Schema):
2817            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2818        elif isinstance(actions[0], exp.Delete):
2819            actions = self.expressions(expression, key="actions", flat=True)
2820        else:
2821            actions = self.expressions(expression, key="actions", flat=True)
2822
2823        exists = " IF EXISTS" if expression.args.get("exists") else ""
2824        only = " ONLY" if expression.args.get("only") else ""
2825        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
2826
2827    def add_column_sql(self, expression: exp.AlterTable) -> str:
2828        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2829            return self.expressions(
2830                expression,
2831                key="actions",
2832                prefix="ADD COLUMN ",
2833            )
2834        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
2835
2836    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2837        expressions = self.expressions(expression)
2838        exists = " IF EXISTS " if expression.args.get("exists") else " "
2839        return f"DROP{exists}{expressions}"
2840
2841    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2842        return f"ADD {self.expressions(expression)}"
2843
2844    def distinct_sql(self, expression: exp.Distinct) -> str:
2845        this = self.expressions(expression, flat=True)
2846
2847        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2848            case = exp.case()
2849            for arg in expression.expressions:
2850                case = case.when(arg.is_(exp.null()), exp.null())
2851            this = self.sql(case.else_(f"({this})"))
2852
2853        this = f" {this}" if this else ""
2854
2855        on = self.sql(expression, "on")
2856        on = f" ON {on}" if on else ""
2857        return f"DISTINCT{this}{on}"
2858
2859    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2860        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
2861
2862    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2863        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
2864
2865    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2866        this_sql = self.sql(expression, "this")
2867        expression_sql = self.sql(expression, "expression")
2868        kind = "MAX" if expression.args.get("max") else "MIN"
2869        return f"{this_sql} HAVING {kind} {expression_sql}"
2870
2871    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
2872        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
2873            # The first modifier here will be the one closest to the AggFunc's arg
2874            mods = sorted(
2875                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
2876                key=lambda x: 0
2877                if isinstance(x, exp.HavingMax)
2878                else (1 if isinstance(x, exp.Order) else 2),
2879            )
2880
2881            if mods:
2882                mod = mods[0]
2883                this = expression.__class__(this=mod.this.copy())
2884                this.meta["inline"] = True
2885                mod.this.replace(this)
2886                return self.sql(expression.this)
2887
2888            agg_func = expression.find(exp.AggFunc)
2889
2890            if agg_func:
2891                return self.sql(agg_func)[:-1] + f" {text})"
2892
2893        return f"{self.sql(expression, 'this')} {text}"
2894
2895    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2896        return self.sql(
2897            exp.Cast(
2898                this=exp.Div(this=expression.this, expression=expression.expression),
2899                to=exp.DataType(this=exp.DataType.Type.INT),
2900            )
2901        )
2902
2903    def dpipe_sql(self, expression: exp.DPipe) -> str:
2904        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2905            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2906        return self.binary(expression, "||")
2907
2908    def div_sql(self, expression: exp.Div) -> str:
2909        l, r = expression.left, expression.right
2910
2911        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2912            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2913
2914        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2915            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
2916                *exp.DataType.FLOAT_TYPES
2917            ):
2918                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2919
2920        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2921            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2922                return self.sql(
2923                    exp.cast(
2924                        l / r,
2925                        to=exp.DataType.Type.BIGINT,
2926                    )
2927                )
2928
2929        return self.binary(expression, "/")
2930
2931    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2932        return self.binary(expression, "OVERLAPS")
2933
2934    def distance_sql(self, expression: exp.Distance) -> str:
2935        return self.binary(expression, "<->")
2936
2937    def dot_sql(self, expression: exp.Dot) -> str:
2938        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
2939
2940    def eq_sql(self, expression: exp.EQ) -> str:
2941        return self.binary(expression, "=")
2942
2943    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
2944        return self.binary(expression, ":=")
2945
2946    def escape_sql(self, expression: exp.Escape) -> str:
2947        return self.binary(expression, "ESCAPE")
2948
2949    def glob_sql(self, expression: exp.Glob) -> str:
2950        return self.binary(expression, "GLOB")
2951
2952    def gt_sql(self, expression: exp.GT) -> str:
2953        return self.binary(expression, ">")
2954
2955    def gte_sql(self, expression: exp.GTE) -> str:
2956        return self.binary(expression, ">=")
2957
2958    def ilike_sql(self, expression: exp.ILike) -> str:
2959        return self.binary(expression, "ILIKE")
2960
2961    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2962        return self.binary(expression, "ILIKE ANY")
2963
2964    def is_sql(self, expression: exp.Is) -> str:
2965        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2966            return self.sql(
2967                expression.this if expression.expression.this else exp.not_(expression.this)
2968            )
2969        return self.binary(expression, "IS")
2970
2971    def like_sql(self, expression: exp.Like) -> str:
2972        return self.binary(expression, "LIKE")
2973
2974    def likeany_sql(self, expression: exp.LikeAny) -> str:
2975        return self.binary(expression, "LIKE ANY")
2976
2977    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2978        return self.binary(expression, "SIMILAR TO")
2979
2980    def lt_sql(self, expression: exp.LT) -> str:
2981        return self.binary(expression, "<")
2982
2983    def lte_sql(self, expression: exp.LTE) -> str:
2984        return self.binary(expression, "<=")
2985
2986    def mod_sql(self, expression: exp.Mod) -> str:
2987        return self.binary(expression, "%")
2988
2989    def mul_sql(self, expression: exp.Mul) -> str:
2990        return self.binary(expression, "*")
2991
2992    def neq_sql(self, expression: exp.NEQ) -> str:
2993        return self.binary(expression, "<>")
2994
2995    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
2996        return self.binary(expression, "IS NOT DISTINCT FROM")
2997
2998    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
2999        return self.binary(expression, "IS DISTINCT FROM")
3000
3001    def or_sql(self, expression: exp.Or) -> str:
3002        return self.connector_sql(expression, "OR")
3003
3004    def slice_sql(self, expression: exp.Slice) -> str:
3005        return self.binary(expression, ":")
3006
3007    def sub_sql(self, expression: exp.Sub) -> str:
3008        return self.binary(expression, "-")
3009
3010    def trycast_sql(self, expression: exp.TryCast) -> str:
3011        return self.cast_sql(expression, safe_prefix="TRY_")
3012
3013    def log_sql(self, expression: exp.Log) -> str:
3014        this = expression.this
3015        expr = expression.expression
3016
3017        if not self.dialect.LOG_BASE_FIRST:
3018            this, expr = expr, this
3019
3020        return self.func("LOG", this, expr)
3021
3022    def use_sql(self, expression: exp.Use) -> str:
3023        kind = self.sql(expression, "kind")
3024        kind = f" {kind}" if kind else ""
3025        this = self.sql(expression, "this")
3026        this = f" {this}" if this else ""
3027        return f"USE{kind}{this}"
3028
3029    def binary(self, expression: exp.Binary, op: str) -> str:
3030        op = self.maybe_comment(op, comments=expression.comments)
3031        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
3032
3033    def function_fallback_sql(self, expression: exp.Func) -> str:
3034        args = []
3035
3036        for key in expression.arg_types:
3037            arg_value = expression.args.get(key)
3038
3039            if isinstance(arg_value, list):
3040                for value in arg_value:
3041                    args.append(value)
3042            elif arg_value is not None:
3043                args.append(arg_value)
3044
3045        if self.normalize_functions:
3046            name = expression.sql_name()
3047        else:
3048            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3049
3050        return self.func(name, *args)
3051
3052    def func(
3053        self,
3054        name: str,
3055        *args: t.Optional[exp.Expression | str],
3056        prefix: str = "(",
3057        suffix: str = ")",
3058    ) -> str:
3059        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
3060
3061    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3062        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3063        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3064            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3065        return ", ".join(arg_sqls)
3066
3067    def text_width(self, args: t.Iterable) -> int:
3068        return sum(len(arg) for arg in args)
3069
3070    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3071        return format_time(
3072            self.sql(expression, "format"),
3073            self.dialect.INVERSE_TIME_MAPPING,
3074            self.dialect.INVERSE_TIME_TRIE,
3075        )
3076
3077    def expressions(
3078        self,
3079        expression: t.Optional[exp.Expression] = None,
3080        key: t.Optional[str] = None,
3081        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3082        flat: bool = False,
3083        indent: bool = True,
3084        skip_first: bool = False,
3085        sep: str = ", ",
3086        prefix: str = "",
3087    ) -> str:
3088        expressions = expression.args.get(key or "expressions") if expression else sqls
3089
3090        if not expressions:
3091            return ""
3092
3093        if flat:
3094            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3095
3096        num_sqls = len(expressions)
3097
3098        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3099        pad = " " * self.pad
3100        stripped_sep = sep.strip()
3101
3102        result_sqls = []
3103        for i, e in enumerate(expressions):
3104            sql = self.sql(e, comment=False)
3105            if not sql:
3106                continue
3107
3108            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3109
3110            if self.pretty:
3111                if self.leading_comma:
3112                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3113                else:
3114                    result_sqls.append(
3115                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3116                    )
3117            else:
3118                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3119
3120        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3121        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
3122
3123    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3124        flat = flat or isinstance(expression.parent, exp.Properties)
3125        expressions_sql = self.expressions(expression, flat=flat)
3126        if flat:
3127            return f"{op} {expressions_sql}"
3128        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3129
3130    def naked_property(self, expression: exp.Property) -> str:
3131        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3132        if not property_name:
3133            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3134        return f"{property_name} {self.sql(expression, 'this')}"
3135
3136    def set_operation(self, expression: exp.Union, op: str) -> str:
3137        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
3138        op = self.seg(op)
3139        return self.query_modifiers(
3140            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
3141        )
3142
3143    def tag_sql(self, expression: exp.Tag) -> str:
3144        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3145
3146    def token_sql(self, token_type: TokenType) -> str:
3147        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3148
3149    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3150        this = self.sql(expression, "this")
3151        expressions = self.no_identify(self.expressions, expression)
3152        expressions = (
3153            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3154        )
3155        return f"{this}{expressions}"
3156
3157    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3158        this = self.sql(expression, "this")
3159        expressions = self.expressions(expression, flat=True)
3160        return f"{this}({expressions})"
3161
3162    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3163        return self.binary(expression, "=>")
3164
3165    def when_sql(self, expression: exp.When) -> str:
3166        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3167        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3168        condition = self.sql(expression, "condition")
3169        condition = f" AND {condition}" if condition else ""
3170
3171        then_expression = expression.args.get("then")
3172        if isinstance(then_expression, exp.Insert):
3173            then = f"INSERT {self.sql(then_expression, 'this')}"
3174            if "expression" in then_expression.args:
3175                then += f" VALUES {self.sql(then_expression, 'expression')}"
3176        elif isinstance(then_expression, exp.Update):
3177            if isinstance(then_expression.args.get("expressions"), exp.Star):
3178                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3179            else:
3180                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3181        else:
3182            then = self.sql(then_expression)
3183        return f"WHEN {matched}{source}{condition} THEN {then}"
3184
3185    def merge_sql(self, expression: exp.Merge) -> str:
3186        table = expression.this
3187        table_alias = ""
3188
3189        hints = table.args.get("hints")
3190        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3191            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3192            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3193
3194        this = self.sql(table)
3195        using = f"USING {self.sql(expression, 'using')}"
3196        on = f"ON {self.sql(expression, 'on')}"
3197        expressions = self.expressions(expression, sep=" ")
3198
3199        return self.prepend_ctes(
3200            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3201        )
3202
3203    def tochar_sql(self, expression: exp.ToChar) -> str:
3204        if expression.args.get("format"):
3205            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3206
3207        return self.sql(exp.cast(expression.this, "text"))
3208
3209    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3210        this = self.sql(expression, "this")
3211        kind = self.sql(expression, "kind")
3212        settings_sql = self.expressions(expression, key="settings", sep=" ")
3213        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3214        return f"{this}({kind}{args})"
3215
3216    def dictrange_sql(self, expression: exp.DictRange) -> str:
3217        this = self.sql(expression, "this")
3218        max = self.sql(expression, "max")
3219        min = self.sql(expression, "min")
3220        return f"{this}(MIN {min} MAX {max})"
3221
3222    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3223        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3224
3225    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3226        return ""
3227
3228    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3229        expressions = self.expressions(expression, key="expressions", flat=True)
3230        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3231        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3232        buckets = self.sql(expression, "buckets")
3233        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3234
3235    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3236        this = self.sql(expression, "this")
3237        having = self.sql(expression, "having")
3238
3239        if having:
3240            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3241
3242        return self.func("ANY_VALUE", this)
3243
3244    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3245        transform = self.func("TRANSFORM", *expression.expressions)
3246        row_format_before = self.sql(expression, "row_format_before")
3247        row_format_before = f" {row_format_before}" if row_format_before else ""
3248        record_writer = self.sql(expression, "record_writer")
3249        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3250        using = f" USING {self.sql(expression, 'command_script')}"
3251        schema = self.sql(expression, "schema")
3252        schema = f" AS {schema}" if schema else ""
3253        row_format_after = self.sql(expression, "row_format_after")
3254        row_format_after = f" {row_format_after}" if row_format_after else ""
3255        record_reader = self.sql(expression, "record_reader")
3256        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3257        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3258
3259    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3260        key_block_size = self.sql(expression, "key_block_size")
3261        if key_block_size:
3262            return f"KEY_BLOCK_SIZE = {key_block_size}"
3263
3264        using = self.sql(expression, "using")
3265        if using:
3266            return f"USING {using}"
3267
3268        parser = self.sql(expression, "parser")
3269        if parser:
3270            return f"WITH PARSER {parser}"
3271
3272        comment = self.sql(expression, "comment")
3273        if comment:
3274            return f"COMMENT {comment}"
3275
3276        visible = expression.args.get("visible")
3277        if visible is not None:
3278            return "VISIBLE" if visible else "INVISIBLE"
3279
3280        engine_attr = self.sql(expression, "engine_attr")
3281        if engine_attr:
3282            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3283
3284        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3285        if secondary_engine_attr:
3286            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3287
3288        self.unsupported("Unsupported index constraint option.")
3289        return ""
3290
3291    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3292        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3293        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3294
3295    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3296        kind = self.sql(expression, "kind")
3297        kind = f"{kind} INDEX" if kind else "INDEX"
3298        this = self.sql(expression, "this")
3299        this = f" {this}" if this else ""
3300        index_type = self.sql(expression, "index_type")
3301        index_type = f" USING {index_type}" if index_type else ""
3302        schema = self.sql(expression, "schema")
3303        schema = f" {schema}" if schema else ""
3304        options = self.expressions(expression, key="options", sep=" ")
3305        options = f" {options}" if options else ""
3306        return f"{kind}{this}{index_type}{schema}{options}"
3307
3308    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3309        if self.NVL2_SUPPORTED:
3310            return self.function_fallback_sql(expression)
3311
3312        case = exp.Case().when(
3313            expression.this.is_(exp.null()).not_(copy=False),
3314            expression.args["true"],
3315            copy=False,
3316        )
3317        else_cond = expression.args.get("false")
3318        if else_cond:
3319            case.else_(else_cond, copy=False)
3320
3321        return self.sql(case)
3322
3323    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3324        this = self.sql(expression, "this")
3325        expr = self.sql(expression, "expression")
3326        iterator = self.sql(expression, "iterator")
3327        condition = self.sql(expression, "condition")
3328        condition = f" IF {condition}" if condition else ""
3329        return f"{this} FOR {expr} IN {iterator}{condition}"
3330
3331    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3332        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
3333
3334    def opclass_sql(self, expression: exp.Opclass) -> str:
3335        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
3336
3337    def predict_sql(self, expression: exp.Predict) -> str:
3338        model = self.sql(expression, "this")
3339        model = f"MODEL {model}"
3340        table = self.sql(expression, "expression")
3341        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3342        parameters = self.sql(expression, "params_struct")
3343        return self.func("PREDICT", model, table, parameters or None)
3344
3345    def forin_sql(self, expression: exp.ForIn) -> str:
3346        this = self.sql(expression, "this")
3347        expression_sql = self.sql(expression, "expression")
3348        return f"FOR {this} DO {expression_sql}"
3349
3350    def refresh_sql(self, expression: exp.Refresh) -> str:
3351        this = self.sql(expression, "this")
3352        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3353        return f"REFRESH {table}{this}"
3354
3355    def operator_sql(self, expression: exp.Operator) -> str:
3356        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
3357
3358    def toarray_sql(self, expression: exp.ToArray) -> str:
3359        arg = expression.this
3360        if not arg.type:
3361            from sqlglot.optimizer.annotate_types import annotate_types
3362
3363            arg = annotate_types(arg)
3364
3365        if arg.is_type(exp.DataType.Type.ARRAY):
3366            return self.sql(arg)
3367
3368        cond_for_null = arg.is_(exp.null())
3369        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3370
3371    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3372        this = expression.this
3373        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3374            return self.sql(this)
3375
3376        return self.sql(exp.cast(this, "time"))
3377
3378    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3379        this = expression.this
3380        time_format = self.format_time(expression)
3381
3382        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3383            return self.sql(
3384                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3385            )
3386
3387        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3388            return self.sql(this)
3389
3390        return self.sql(exp.cast(this, "date"))
3391
3392    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3393        return self.sql(
3394            exp.func(
3395                "DATEDIFF",
3396                expression.this,
3397                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3398                "day",
3399            )
3400        )
3401
3402    def lastday_sql(self, expression: exp.LastDay) -> str:
3403        if self.LAST_DAY_SUPPORTS_DATE_PART:
3404            return self.function_fallback_sql(expression)
3405
3406        unit = expression.text("unit")
3407        if unit and unit != "MONTH":
3408            self.unsupported("Date parts are not supported in LAST_DAY.")
3409
3410        return self.func("LAST_DAY", expression.this)
3411
3412    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3413        if self.CAN_IMPLEMENT_ARRAY_ANY:
3414            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3415            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3416            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3417            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3418
3419        from sqlglot.dialects import Dialect
3420
3421        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3422        if self.dialect.__class__ != Dialect:
3423            self.unsupported("ARRAY_ANY is unsupported")
3424
3425        return self.function_fallback_sql(expression)
3426
3427    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
3428        this = expression.this
3429        if isinstance(this, exp.JSONPathWildcard):
3430            this = self.json_path_part(this)
3431            return f".{this}" if this else ""
3432
3433        if exp.SAFE_IDENTIFIER_RE.match(this):
3434            return f".{this}"
3435
3436        this = self.json_path_part(this)
3437        return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}"
3438
3439    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
3440        this = self.json_path_part(expression.this)
3441        return f"[{this}]" if this else ""
3442
3443    def _simplify_unless_literal(self, expression: E) -> E:
3444        if not isinstance(expression, exp.Literal):
3445            from sqlglot.optimizer.simplify import simplify
3446
3447            expression = simplify(expression, dialect=self.dialect)
3448
3449        return expression
3450
3451    def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]:
3452        return [
3453            exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string(""))
3454            for value in values
3455            if value
3456        ]
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 self,
  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 self, e: "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 self, e: "EXTERNAL",
  95        exp.HeapProperty: lambda self, e: "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 self, e: f"{'NO ' if e.args.get('no') else ''}LOG",
 109        exp.MaterializedProperty: lambda self, e: "MATERIALIZED",
 110        exp.NonClusteredColumnConstraint: lambda self,
 111        e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})",
 112        exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX",
 113        exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION",
 114        exp.OnCommitProperty: lambda self,
 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 self, 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 self, e: e.name,
 128        exp.SqlSecurityProperty: lambda self,
 129        e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}",
 130        exp.StabilityProperty: lambda self, e: e.name,
 131        exp.TemporaryProperty: lambda self, e: "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 self, e: "TRANSIENT",
 137        exp.UppercaseColumnConstraint: lambda self, e: "UPPERCASE",
 138        exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]),
 139        exp.VolatileProperty: lambda self, e: "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.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    KEY_VALUE_DEFINITIONS = (exp.EQ, exp.PropertyEQ, exp.Slice)
 467
 468    SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 469
 470    __slots__ = (
 471        "pretty",
 472        "identify",
 473        "normalize",
 474        "pad",
 475        "_indent",
 476        "normalize_functions",
 477        "unsupported_level",
 478        "max_unsupported",
 479        "leading_comma",
 480        "max_text_width",
 481        "comments",
 482        "dialect",
 483        "unsupported_messages",
 484        "_escaped_quote_end",
 485        "_escaped_identifier_end",
 486    )
 487
 488    def __init__(
 489        self,
 490        pretty: t.Optional[bool] = None,
 491        identify: str | bool = False,
 492        normalize: bool = False,
 493        pad: int = 2,
 494        indent: int = 2,
 495        normalize_functions: t.Optional[str | bool] = None,
 496        unsupported_level: ErrorLevel = ErrorLevel.WARN,
 497        max_unsupported: int = 3,
 498        leading_comma: bool = False,
 499        max_text_width: int = 80,
 500        comments: bool = True,
 501        dialect: DialectType = None,
 502    ):
 503        import sqlglot
 504        from sqlglot.dialects import Dialect
 505
 506        self.pretty = pretty if pretty is not None else sqlglot.pretty
 507        self.identify = identify
 508        self.normalize = normalize
 509        self.pad = pad
 510        self._indent = indent
 511        self.unsupported_level = unsupported_level
 512        self.max_unsupported = max_unsupported
 513        self.leading_comma = leading_comma
 514        self.max_text_width = max_text_width
 515        self.comments = comments
 516        self.dialect = Dialect.get_or_raise(dialect)
 517
 518        # This is both a Dialect property and a Generator argument, so we prioritize the latter
 519        self.normalize_functions = (
 520            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
 521        )
 522
 523        self.unsupported_messages: t.List[str] = []
 524        self._escaped_quote_end: str = (
 525            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
 526        )
 527        self._escaped_identifier_end: str = (
 528            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
 529        )
 530
 531    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
 532        """
 533        Generates the SQL string corresponding to the given syntax tree.
 534
 535        Args:
 536            expression: The syntax tree.
 537            copy: Whether to copy the expression. The generator performs mutations so
 538                it is safer to copy.
 539
 540        Returns:
 541            The SQL string corresponding to `expression`.
 542        """
 543        if copy:
 544            expression = expression.copy()
 545
 546        expression = self.preprocess(expression)
 547
 548        self.unsupported_messages = []
 549        sql = self.sql(expression).strip()
 550
 551        if self.pretty:
 552            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
 553
 554        if self.unsupported_level == ErrorLevel.IGNORE:
 555            return sql
 556
 557        if self.unsupported_level == ErrorLevel.WARN:
 558            for msg in self.unsupported_messages:
 559                logger.warning(msg)
 560        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
 561            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
 562
 563        return sql
 564
 565    def preprocess(self, expression: exp.Expression) -> exp.Expression:
 566        """Apply generic preprocessing transformations to a given expression."""
 567        if (
 568            not expression.parent
 569            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
 570            and any(node.parent is not expression for node in expression.find_all(exp.With))
 571        ):
 572            from sqlglot.transforms import move_ctes_to_top_level
 573
 574            expression = move_ctes_to_top_level(expression)
 575
 576        if self.ENSURE_BOOLS:
 577            from sqlglot.transforms import ensure_bools
 578
 579            expression = ensure_bools(expression)
 580
 581        return expression
 582
 583    def unsupported(self, message: str) -> None:
 584        if self.unsupported_level == ErrorLevel.IMMEDIATE:
 585            raise UnsupportedError(message)
 586        self.unsupported_messages.append(message)
 587
 588    def sep(self, sep: str = " ") -> str:
 589        return f"{sep.strip()}\n" if self.pretty else sep
 590
 591    def seg(self, sql: str, sep: str = " ") -> str:
 592        return f"{self.sep(sep)}{sql}"
 593
 594    def pad_comment(self, comment: str) -> str:
 595        comment = " " + comment if comment[0].strip() else comment
 596        comment = comment + " " if comment[-1].strip() else comment
 597        return comment
 598
 599    def maybe_comment(
 600        self,
 601        sql: str,
 602        expression: t.Optional[exp.Expression] = None,
 603        comments: t.Optional[t.List[str]] = None,
 604    ) -> str:
 605        comments = (
 606            ((expression and expression.comments) if comments is None else comments)  # type: ignore
 607            if self.comments
 608            else None
 609        )
 610
 611        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
 612            return sql
 613
 614        comments_sql = " ".join(
 615            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
 616        )
 617
 618        if not comments_sql:
 619            return sql
 620
 621        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
 622            return (
 623                f"{self.sep()}{comments_sql}{sql}"
 624                if sql[0].isspace()
 625                else f"{comments_sql}{self.sep()}{sql}"
 626            )
 627
 628        return f"{sql} {comments_sql}"
 629
 630    def wrap(self, expression: exp.Expression | str) -> str:
 631        this_sql = self.indent(
 632            (
 633                self.sql(expression)
 634                if isinstance(expression, (exp.Select, exp.Union))
 635                else self.sql(expression, "this")
 636            ),
 637            level=1,
 638            pad=0,
 639        )
 640        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
 641
 642    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
 643        original = self.identify
 644        self.identify = False
 645        result = func(*args, **kwargs)
 646        self.identify = original
 647        return result
 648
 649    def normalize_func(self, name: str) -> str:
 650        if self.normalize_functions == "upper" or self.normalize_functions is True:
 651            return name.upper()
 652        if self.normalize_functions == "lower":
 653            return name.lower()
 654        return name
 655
 656    def indent(
 657        self,
 658        sql: str,
 659        level: int = 0,
 660        pad: t.Optional[int] = None,
 661        skip_first: bool = False,
 662        skip_last: bool = False,
 663    ) -> str:
 664        if not self.pretty:
 665            return sql
 666
 667        pad = self.pad if pad is None else pad
 668        lines = sql.split("\n")
 669
 670        return "\n".join(
 671            (
 672                line
 673                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
 674                else f"{' ' * (level * self._indent + pad)}{line}"
 675            )
 676            for i, line in enumerate(lines)
 677        )
 678
 679    def sql(
 680        self,
 681        expression: t.Optional[str | exp.Expression],
 682        key: t.Optional[str] = None,
 683        comment: bool = True,
 684    ) -> str:
 685        if not expression:
 686            return ""
 687
 688        if isinstance(expression, str):
 689            return expression
 690
 691        if key:
 692            value = expression.args.get(key)
 693            if value:
 694                return self.sql(value)
 695            return ""
 696
 697        transform = self.TRANSFORMS.get(expression.__class__)
 698
 699        if callable(transform):
 700            sql = transform(self, expression)
 701        elif isinstance(expression, exp.Expression):
 702            exp_handler_name = f"{expression.key}_sql"
 703
 704            if hasattr(self, exp_handler_name):
 705                sql = getattr(self, exp_handler_name)(expression)
 706            elif isinstance(expression, exp.Func):
 707                sql = self.function_fallback_sql(expression)
 708            elif isinstance(expression, exp.Property):
 709                sql = self.property_sql(expression)
 710            else:
 711                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
 712        else:
 713            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
 714
 715        return self.maybe_comment(sql, expression) if self.comments and comment else sql
 716
 717    def uncache_sql(self, expression: exp.Uncache) -> str:
 718        table = self.sql(expression, "this")
 719        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
 720        return f"UNCACHE TABLE{exists_sql} {table}"
 721
 722    def cache_sql(self, expression: exp.Cache) -> str:
 723        lazy = " LAZY" if expression.args.get("lazy") else ""
 724        table = self.sql(expression, "this")
 725        options = expression.args.get("options")
 726        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
 727        sql = self.sql(expression, "expression")
 728        sql = f" AS{self.sep()}{sql}" if sql else ""
 729        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
 730        return self.prepend_ctes(expression, sql)
 731
 732    def characterset_sql(self, expression: exp.CharacterSet) -> str:
 733        if isinstance(expression.parent, exp.Cast):
 734            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
 735        default = "DEFAULT " if expression.args.get("default") else ""
 736        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
 737
 738    def column_sql(self, expression: exp.Column) -> str:
 739        join_mark = " (+)" if expression.args.get("join_mark") else ""
 740
 741        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
 742            join_mark = ""
 743            self.unsupported("Outer join syntax using the (+) operator is not supported.")
 744
 745        column = ".".join(
 746            self.sql(part)
 747            for part in (
 748                expression.args.get("catalog"),
 749                expression.args.get("db"),
 750                expression.args.get("table"),
 751                expression.args.get("this"),
 752            )
 753            if part
 754        )
 755
 756        return f"{column}{join_mark}"
 757
 758    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
 759        this = self.sql(expression, "this")
 760        this = f" {this}" if this else ""
 761        position = self.sql(expression, "position")
 762        return f"{position}{this}"
 763
 764    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
 765        column = self.sql(expression, "this")
 766        kind = self.sql(expression, "kind")
 767        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
 768        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
 769        kind = f"{sep}{kind}" if kind else ""
 770        constraints = f" {constraints}" if constraints else ""
 771        position = self.sql(expression, "position")
 772        position = f" {position}" if position else ""
 773
 774        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
 775            kind = ""
 776
 777        return f"{exists}{column}{kind}{constraints}{position}"
 778
 779    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
 780        this = self.sql(expression, "this")
 781        kind_sql = self.sql(expression, "kind").strip()
 782        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
 783
 784    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
 785        this = self.sql(expression, "this")
 786        if expression.args.get("not_null"):
 787            persisted = " PERSISTED NOT NULL"
 788        elif expression.args.get("persisted"):
 789            persisted = " PERSISTED"
 790        else:
 791            persisted = ""
 792        return f"AS {this}{persisted}"
 793
 794    def autoincrementcolumnconstraint_sql(self, _) -> str:
 795        return self.token_sql(TokenType.AUTO_INCREMENT)
 796
 797    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
 798        if isinstance(expression.this, list):
 799            this = self.wrap(self.expressions(expression, key="this", flat=True))
 800        else:
 801            this = self.sql(expression, "this")
 802
 803        return f"COMPRESS {this}"
 804
 805    def generatedasidentitycolumnconstraint_sql(
 806        self, expression: exp.GeneratedAsIdentityColumnConstraint
 807    ) -> str:
 808        this = ""
 809        if expression.this is not None:
 810            on_null = " ON NULL" if expression.args.get("on_null") else ""
 811            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
 812
 813        start = expression.args.get("start")
 814        start = f"START WITH {start}" if start else ""
 815        increment = expression.args.get("increment")
 816        increment = f" INCREMENT BY {increment}" if increment else ""
 817        minvalue = expression.args.get("minvalue")
 818        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
 819        maxvalue = expression.args.get("maxvalue")
 820        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
 821        cycle = expression.args.get("cycle")
 822        cycle_sql = ""
 823
 824        if cycle is not None:
 825            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
 826            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
 827
 828        sequence_opts = ""
 829        if start or increment or cycle_sql:
 830            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
 831            sequence_opts = f" ({sequence_opts.strip()})"
 832
 833        expr = self.sql(expression, "expression")
 834        expr = f"({expr})" if expr else "IDENTITY"
 835
 836        return f"GENERATED{this} AS {expr}{sequence_opts}"
 837
 838    def generatedasrowcolumnconstraint_sql(
 839        self, expression: exp.GeneratedAsRowColumnConstraint
 840    ) -> str:
 841        start = "START" if expression.args.get("start") else "END"
 842        hidden = " HIDDEN" if expression.args.get("hidden") else ""
 843        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
 844
 845    def periodforsystemtimeconstraint_sql(
 846        self, expression: exp.PeriodForSystemTimeConstraint
 847    ) -> str:
 848        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
 849
 850    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
 851        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
 852
 853    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
 854        return f"AS {self.sql(expression, 'this')}"
 855
 856    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
 857        desc = expression.args.get("desc")
 858        if desc is not None:
 859            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
 860        return "PRIMARY KEY"
 861
 862    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
 863        this = self.sql(expression, "this")
 864        this = f" {this}" if this else ""
 865        index_type = expression.args.get("index_type")
 866        index_type = f" USING {index_type}" if index_type else ""
 867        return f"UNIQUE{this}{index_type}"
 868
 869    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
 870        return self.sql(expression, "this")
 871
 872    def create_sql(self, expression: exp.Create) -> str:
 873        kind = self.sql(expression, "kind")
 874        properties = expression.args.get("properties")
 875        properties_locs = self.locate_properties(properties) if properties else defaultdict()
 876
 877        this = self.createable_sql(expression, properties_locs)
 878
 879        properties_sql = ""
 880        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
 881            exp.Properties.Location.POST_WITH
 882        ):
 883            properties_sql = self.sql(
 884                exp.Properties(
 885                    expressions=[
 886                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
 887                        *properties_locs[exp.Properties.Location.POST_WITH],
 888                    ]
 889                )
 890            )
 891
 892        begin = " BEGIN" if expression.args.get("begin") else ""
 893        end = " END" if expression.args.get("end") else ""
 894
 895        expression_sql = self.sql(expression, "expression")
 896        if expression_sql:
 897            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
 898
 899            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
 900                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
 901                    postalias_props_sql = self.properties(
 902                        exp.Properties(
 903                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
 904                        ),
 905                        wrapped=False,
 906                    )
 907                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
 908                else:
 909                    expression_sql = f" AS{expression_sql}"
 910
 911        postindex_props_sql = ""
 912        if properties_locs.get(exp.Properties.Location.POST_INDEX):
 913            postindex_props_sql = self.properties(
 914                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
 915                wrapped=False,
 916                prefix=" ",
 917            )
 918
 919        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
 920        indexes = f" {indexes}" if indexes else ""
 921        index_sql = indexes + postindex_props_sql
 922
 923        replace = " OR REPLACE" if expression.args.get("replace") else ""
 924        unique = " UNIQUE" if expression.args.get("unique") else ""
 925
 926        postcreate_props_sql = ""
 927        if properties_locs.get(exp.Properties.Location.POST_CREATE):
 928            postcreate_props_sql = self.properties(
 929                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
 930                sep=" ",
 931                prefix=" ",
 932                wrapped=False,
 933            )
 934
 935        modifiers = "".join((replace, unique, postcreate_props_sql))
 936
 937        postexpression_props_sql = ""
 938        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
 939            postexpression_props_sql = self.properties(
 940                exp.Properties(
 941                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
 942                ),
 943                sep=" ",
 944                prefix=" ",
 945                wrapped=False,
 946            )
 947
 948        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
 949        no_schema_binding = (
 950            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
 951        )
 952
 953        clone = self.sql(expression, "clone")
 954        clone = f" {clone}" if clone else ""
 955
 956        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
 957        return self.prepend_ctes(expression, expression_sql)
 958
 959    def clone_sql(self, expression: exp.Clone) -> str:
 960        this = self.sql(expression, "this")
 961        shallow = "SHALLOW " if expression.args.get("shallow") else ""
 962        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
 963        return f"{shallow}{keyword} {this}"
 964
 965    def describe_sql(self, expression: exp.Describe) -> str:
 966        extended = " EXTENDED" if expression.args.get("extended") else ""
 967        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
 968
 969    def heredoc_sql(self, expression: exp.Heredoc) -> str:
 970        tag = self.sql(expression, "tag")
 971        return f"${tag}${self.sql(expression, 'this')}${tag}$"
 972
 973    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
 974        with_ = self.sql(expression, "with")
 975        if with_:
 976            sql = f"{with_}{self.sep()}{sql}"
 977        return sql
 978
 979    def with_sql(self, expression: exp.With) -> str:
 980        sql = self.expressions(expression, flat=True)
 981        recursive = (
 982            "RECURSIVE "
 983            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
 984            else ""
 985        )
 986
 987        return f"WITH {recursive}{sql}"
 988
 989    def cte_sql(self, expression: exp.CTE) -> str:
 990        alias = self.sql(expression, "alias")
 991        return f"{alias} AS {self.wrap(expression)}"
 992
 993    def tablealias_sql(self, expression: exp.TableAlias) -> str:
 994        alias = self.sql(expression, "this")
 995        columns = self.expressions(expression, key="columns", flat=True)
 996        columns = f"({columns})" if columns else ""
 997
 998        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
 999            columns = ""
1000            self.unsupported("Named columns are not supported in table alias.")
1001
1002        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1003            alias = "_t"
1004
1005        return f"{alias}{columns}"
1006
1007    def bitstring_sql(self, expression: exp.BitString) -> str:
1008        this = self.sql(expression, "this")
1009        if self.dialect.BIT_START:
1010            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1011        return f"{int(this, 2)}"
1012
1013    def hexstring_sql(self, expression: exp.HexString) -> str:
1014        this = self.sql(expression, "this")
1015        if self.dialect.HEX_START:
1016            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1017        return f"{int(this, 16)}"
1018
1019    def bytestring_sql(self, expression: exp.ByteString) -> str:
1020        this = self.sql(expression, "this")
1021        if self.dialect.BYTE_START:
1022            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1023        return this
1024
1025    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1026        this = self.sql(expression, "this")
1027        escape = expression.args.get("escape")
1028
1029        if self.dialect.UNICODE_START:
1030            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1031            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1032
1033        if escape:
1034            pattern = re.compile(rf"{escape.name}(\d+)")
1035        else:
1036            pattern = ESCAPED_UNICODE_RE
1037
1038        this = pattern.sub(r"\\u\1", this)
1039        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1040
1041    def rawstring_sql(self, expression: exp.RawString) -> str:
1042        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1043        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
1044
1045    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1046        this = self.sql(expression, "this")
1047        specifier = self.sql(expression, "expression")
1048        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1049        return f"{this}{specifier}"
1050
1051    def datatype_sql(self, expression: exp.DataType) -> str:
1052        type_value = expression.this
1053
1054        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1055            type_sql = self.sql(expression, "kind")
1056        else:
1057            type_sql = (
1058                self.TYPE_MAPPING.get(type_value, type_value.value)
1059                if isinstance(type_value, exp.DataType.Type)
1060                else type_value
1061            )
1062
1063        nested = ""
1064        interior = self.expressions(expression, flat=True)
1065        values = ""
1066
1067        if interior:
1068            if expression.args.get("nested"):
1069                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1070                if expression.args.get("values") is not None:
1071                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1072                    values = self.expressions(expression, key="values", flat=True)
1073                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1074            elif type_value == exp.DataType.Type.INTERVAL:
1075                nested = f" {interior}"
1076            else:
1077                nested = f"({interior})"
1078
1079        type_sql = f"{type_sql}{nested}{values}"
1080        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1081            exp.DataType.Type.TIMETZ,
1082            exp.DataType.Type.TIMESTAMPTZ,
1083        ):
1084            type_sql = f"{type_sql} WITH TIME ZONE"
1085
1086        return type_sql
1087
1088    def directory_sql(self, expression: exp.Directory) -> str:
1089        local = "LOCAL " if expression.args.get("local") else ""
1090        row_format = self.sql(expression, "row_format")
1091        row_format = f" {row_format}" if row_format else ""
1092        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1093
1094    def delete_sql(self, expression: exp.Delete) -> str:
1095        this = self.sql(expression, "this")
1096        this = f" FROM {this}" if this else ""
1097        using = self.sql(expression, "using")
1098        using = f" USING {using}" if using else ""
1099        where = self.sql(expression, "where")
1100        returning = self.sql(expression, "returning")
1101        limit = self.sql(expression, "limit")
1102        tables = self.expressions(expression, key="tables")
1103        tables = f" {tables}" if tables else ""
1104        if self.RETURNING_END:
1105            expression_sql = f"{this}{using}{where}{returning}{limit}"
1106        else:
1107            expression_sql = f"{returning}{this}{using}{where}{limit}"
1108        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1109
1110    def drop_sql(self, expression: exp.Drop) -> str:
1111        this = self.sql(expression, "this")
1112        kind = expression.args["kind"]
1113        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1114        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1115        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1116        cascade = " CASCADE" if expression.args.get("cascade") else ""
1117        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1118        purge = " PURGE" if expression.args.get("purge") else ""
1119        return (
1120            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1121        )
1122
1123    def except_sql(self, expression: exp.Except) -> str:
1124        return self.prepend_ctes(
1125            expression,
1126            self.set_operation(expression, self.except_op(expression)),
1127        )
1128
1129    def except_op(self, expression: exp.Except) -> str:
1130        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
1131
1132    def fetch_sql(self, expression: exp.Fetch) -> str:
1133        direction = expression.args.get("direction")
1134        direction = f" {direction}" if direction else ""
1135        count = expression.args.get("count")
1136        count = f" {count}" if count else ""
1137        if expression.args.get("percent"):
1138            count = f"{count} PERCENT"
1139        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1140        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1141
1142    def filter_sql(self, expression: exp.Filter) -> str:
1143        if self.AGGREGATE_FILTER_SUPPORTED:
1144            this = self.sql(expression, "this")
1145            where = self.sql(expression, "expression").strip()
1146            return f"{this} FILTER({where})"
1147
1148        agg = expression.this
1149        agg_arg = agg.this
1150        cond = expression.expression.this
1151        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1152        return self.sql(agg)
1153
1154    def hint_sql(self, expression: exp.Hint) -> str:
1155        if not self.QUERY_HINTS:
1156            self.unsupported("Hints are not supported")
1157            return ""
1158
1159        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
1160
1161    def index_sql(self, expression: exp.Index) -> str:
1162        unique = "UNIQUE " if expression.args.get("unique") else ""
1163        primary = "PRIMARY " if expression.args.get("primary") else ""
1164        amp = "AMP " if expression.args.get("amp") else ""
1165        name = self.sql(expression, "this")
1166        name = f"{name} " if name else ""
1167        table = self.sql(expression, "table")
1168        table = f"{self.INDEX_ON} {table}" if table else ""
1169        using = self.sql(expression, "using")
1170        using = f" USING {using}" if using else ""
1171        index = "INDEX " if not table else ""
1172        columns = self.expressions(expression, key="columns", flat=True)
1173        columns = f"({columns})" if columns else ""
1174        partition_by = self.expressions(expression, key="partition_by", flat=True)
1175        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1176        where = self.sql(expression, "where")
1177        include = self.expressions(expression, key="include", flat=True)
1178        if include:
1179            include = f" INCLUDE ({include})"
1180        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
1181
1182    def identifier_sql(self, expression: exp.Identifier) -> str:
1183        text = expression.name
1184        lower = text.lower()
1185        text = lower if self.normalize and not expression.quoted else text
1186        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1187        if (
1188            expression.quoted
1189            or self.dialect.can_identify(text, self.identify)
1190            or lower in self.RESERVED_KEYWORDS
1191            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1192        ):
1193            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1194        return text
1195
1196    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1197        input_format = self.sql(expression, "input_format")
1198        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1199        output_format = self.sql(expression, "output_format")
1200        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1201        return self.sep().join((input_format, output_format))
1202
1203    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1204        string = self.sql(exp.Literal.string(expression.name))
1205        return f"{prefix}{string}"
1206
1207    def partition_sql(self, expression: exp.Partition) -> str:
1208        return f"PARTITION({self.expressions(expression, flat=True)})"
1209
1210    def properties_sql(self, expression: exp.Properties) -> str:
1211        root_properties = []
1212        with_properties = []
1213
1214        for p in expression.expressions:
1215            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1216            if p_loc == exp.Properties.Location.POST_WITH:
1217                with_properties.append(p)
1218            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1219                root_properties.append(p)
1220
1221        return self.root_properties(
1222            exp.Properties(expressions=root_properties)
1223        ) + self.with_properties(exp.Properties(expressions=with_properties))
1224
1225    def root_properties(self, properties: exp.Properties) -> str:
1226        if properties.expressions:
1227            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1228        return ""
1229
1230    def properties(
1231        self,
1232        properties: exp.Properties,
1233        prefix: str = "",
1234        sep: str = ", ",
1235        suffix: str = "",
1236        wrapped: bool = True,
1237    ) -> str:
1238        if properties.expressions:
1239            expressions = self.expressions(properties, sep=sep, indent=False)
1240            if expressions:
1241                expressions = self.wrap(expressions) if wrapped else expressions
1242                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1243        return ""
1244
1245    def with_properties(self, properties: exp.Properties) -> str:
1246        return self.properties(properties, prefix=self.seg("WITH"))
1247
1248    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1249        properties_locs = defaultdict(list)
1250        for p in properties.expressions:
1251            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1252            if p_loc != exp.Properties.Location.UNSUPPORTED:
1253                properties_locs[p_loc].append(p)
1254            else:
1255                self.unsupported(f"Unsupported property {p.key}")
1256
1257        return properties_locs
1258
1259    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1260        if isinstance(expression.this, exp.Dot):
1261            return self.sql(expression, "this")
1262        return f"'{expression.name}'" if string_key else expression.name
1263
1264    def property_sql(self, expression: exp.Property) -> str:
1265        property_cls = expression.__class__
1266        if property_cls == exp.Property:
1267            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1268
1269        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1270        if not property_name:
1271            self.unsupported(f"Unsupported property {expression.key}")
1272
1273        return f"{property_name}={self.sql(expression, 'this')}"
1274
1275    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1276        if self.SUPPORTS_CREATE_TABLE_LIKE:
1277            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1278            options = f" {options}" if options else ""
1279
1280            like = f"LIKE {self.sql(expression, 'this')}{options}"
1281            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1282                like = f"({like})"
1283
1284            return like
1285
1286        if expression.expressions:
1287            self.unsupported("Transpilation of LIKE property options is unsupported")
1288
1289        select = exp.select("*").from_(expression.this).limit(0)
1290        return f"AS {self.sql(select)}"
1291
1292    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1293        no = "NO " if expression.args.get("no") else ""
1294        protection = " PROTECTION" if expression.args.get("protection") else ""
1295        return f"{no}FALLBACK{protection}"
1296
1297    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1298        no = "NO " if expression.args.get("no") else ""
1299        local = expression.args.get("local")
1300        local = f"{local} " if local else ""
1301        dual = "DUAL " if expression.args.get("dual") else ""
1302        before = "BEFORE " if expression.args.get("before") else ""
1303        after = "AFTER " if expression.args.get("after") else ""
1304        return f"{no}{local}{dual}{before}{after}JOURNAL"
1305
1306    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1307        freespace = self.sql(expression, "this")
1308        percent = " PERCENT" if expression.args.get("percent") else ""
1309        return f"FREESPACE={freespace}{percent}"
1310
1311    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1312        if expression.args.get("default"):
1313            property = "DEFAULT"
1314        elif expression.args.get("on"):
1315            property = "ON"
1316        else:
1317            property = "OFF"
1318        return f"CHECKSUM={property}"
1319
1320    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1321        if expression.args.get("no"):
1322            return "NO MERGEBLOCKRATIO"
1323        if expression.args.get("default"):
1324            return "DEFAULT MERGEBLOCKRATIO"
1325
1326        percent = " PERCENT" if expression.args.get("percent") else ""
1327        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1328
1329    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1330        default = expression.args.get("default")
1331        minimum = expression.args.get("minimum")
1332        maximum = expression.args.get("maximum")
1333        if default or minimum or maximum:
1334            if default:
1335                prop = "DEFAULT"
1336            elif minimum:
1337                prop = "MINIMUM"
1338            else:
1339                prop = "MAXIMUM"
1340            return f"{prop} DATABLOCKSIZE"
1341        units = expression.args.get("units")
1342        units = f" {units}" if units else ""
1343        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
1344
1345    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1346        autotemp = expression.args.get("autotemp")
1347        always = expression.args.get("always")
1348        default = expression.args.get("default")
1349        manual = expression.args.get("manual")
1350        never = expression.args.get("never")
1351
1352        if autotemp is not None:
1353            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1354        elif always:
1355            prop = "ALWAYS"
1356        elif default:
1357            prop = "DEFAULT"
1358        elif manual:
1359            prop = "MANUAL"
1360        elif never:
1361            prop = "NEVER"
1362        return f"BLOCKCOMPRESSION={prop}"
1363
1364    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1365        no = expression.args.get("no")
1366        no = " NO" if no else ""
1367        concurrent = expression.args.get("concurrent")
1368        concurrent = " CONCURRENT" if concurrent else ""
1369
1370        for_ = ""
1371        if expression.args.get("for_all"):
1372            for_ = " FOR ALL"
1373        elif expression.args.get("for_insert"):
1374            for_ = " FOR INSERT"
1375        elif expression.args.get("for_none"):
1376            for_ = " FOR NONE"
1377        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
1378
1379    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1380        if isinstance(expression.this, list):
1381            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1382        if expression.this:
1383            modulus = self.sql(expression, "this")
1384            remainder = self.sql(expression, "expression")
1385            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1386
1387        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1388        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1389        return f"FROM ({from_expressions}) TO ({to_expressions})"
1390
1391    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1392        this = self.sql(expression, "this")
1393
1394        for_values_or_default = expression.expression
1395        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1396            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1397        else:
1398            for_values_or_default = " DEFAULT"
1399
1400        return f"PARTITION OF {this}{for_values_or_default}"
1401
1402    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1403        kind = expression.args.get("kind")
1404        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1405        for_or_in = expression.args.get("for_or_in")
1406        for_or_in = f" {for_or_in}" if for_or_in else ""
1407        lock_type = expression.args.get("lock_type")
1408        override = " OVERRIDE" if expression.args.get("override") else ""
1409        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1410
1411    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1412        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1413        statistics = expression.args.get("statistics")
1414        statistics_sql = ""
1415        if statistics is not None:
1416            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1417        return f"{data_sql}{statistics_sql}"
1418
1419    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1420        sql = "WITH(SYSTEM_VERSIONING=ON"
1421
1422        if expression.this:
1423            history_table = self.sql(expression, "this")
1424            sql = f"{sql}(HISTORY_TABLE={history_table}"
1425
1426            if expression.expression:
1427                data_consistency_check = self.sql(expression, "expression")
1428                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1429
1430            sql = f"{sql})"
1431
1432        return f"{sql})"
1433
1434    def insert_sql(self, expression: exp.Insert) -> str:
1435        overwrite = expression.args.get("overwrite")
1436
1437        if isinstance(expression.this, exp.Directory):
1438            this = " OVERWRITE" if overwrite else " INTO"
1439        else:
1440            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1441
1442        alternative = expression.args.get("alternative")
1443        alternative = f" OR {alternative}" if alternative else ""
1444        ignore = " IGNORE" if expression.args.get("ignore") else ""
1445
1446        this = f"{this} {self.sql(expression, 'this')}"
1447
1448        exists = " IF EXISTS" if expression.args.get("exists") else ""
1449        partition_sql = (
1450            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1451        )
1452        where = self.sql(expression, "where")
1453        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1454        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1455        conflict = self.sql(expression, "conflict")
1456        by_name = " BY NAME" if expression.args.get("by_name") else ""
1457        returning = self.sql(expression, "returning")
1458
1459        if self.RETURNING_END:
1460            expression_sql = f"{expression_sql}{conflict}{returning}"
1461        else:
1462            expression_sql = f"{returning}{expression_sql}{conflict}"
1463
1464        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1465        return self.prepend_ctes(expression, sql)
1466
1467    def intersect_sql(self, expression: exp.Intersect) -> str:
1468        return self.prepend_ctes(
1469            expression,
1470            self.set_operation(expression, self.intersect_op(expression)),
1471        )
1472
1473    def intersect_op(self, expression: exp.Intersect) -> str:
1474        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
1475
1476    def introducer_sql(self, expression: exp.Introducer) -> str:
1477        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
1478
1479    def kill_sql(self, expression: exp.Kill) -> str:
1480        kind = self.sql(expression, "kind")
1481        kind = f" {kind}" if kind else ""
1482        this = self.sql(expression, "this")
1483        this = f" {this}" if this else ""
1484        return f"KILL{kind}{this}"
1485
1486    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1487        return expression.name
1488
1489    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1490        return expression.name
1491
1492    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1493        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1494        constraint = self.sql(expression, "constraint")
1495        if constraint:
1496            constraint = f"ON CONSTRAINT {constraint}"
1497        key = self.expressions(expression, key="key", flat=True)
1498        do = "" if expression.args.get("duplicate") else " DO "
1499        nothing = "NOTHING" if expression.args.get("nothing") else ""
1500        expressions = self.expressions(expression, flat=True)
1501        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1502        if expressions:
1503            expressions = f"UPDATE {set_keyword}{expressions}"
1504        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
1505
1506    def returning_sql(self, expression: exp.Returning) -> str:
1507        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
1508
1509    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1510        fields = expression.args.get("fields")
1511        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1512        escaped = expression.args.get("escaped")
1513        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1514        items = expression.args.get("collection_items")
1515        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1516        keys = expression.args.get("map_keys")
1517        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1518        lines = expression.args.get("lines")
1519        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1520        null = expression.args.get("null")
1521        null = f" NULL DEFINED AS {null}" if null else ""
1522        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1523
1524    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1525        return f"WITH ({self.expressions(expression, flat=True)})"
1526
1527    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1528        this = f"{self.sql(expression, 'this')} INDEX"
1529        target = self.sql(expression, "target")
1530        target = f" FOR {target}" if target else ""
1531        return f"{this}{target} ({self.expressions(expression, flat=True)})"
1532
1533    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1534        this = self.sql(expression, "this")
1535        kind = self.sql(expression, "kind")
1536        expr = self.sql(expression, "expression")
1537        return f"{this} ({kind} => {expr})"
1538
1539    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1540        table = ".".join(
1541            self.sql(part)
1542            for part in (
1543                expression.args.get("catalog"),
1544                expression.args.get("db"),
1545                expression.args.get("this"),
1546            )
1547            if part is not None
1548        )
1549
1550        version = self.sql(expression, "version")
1551        version = f" {version}" if version else ""
1552        alias = self.sql(expression, "alias")
1553        alias = f"{sep}{alias}" if alias else ""
1554        hints = self.expressions(expression, key="hints", sep=" ")
1555        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1556        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1557        pivots = f" {pivots}" if pivots else ""
1558        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1559        laterals = self.expressions(expression, key="laterals", sep="")
1560
1561        file_format = self.sql(expression, "format")
1562        if file_format:
1563            pattern = self.sql(expression, "pattern")
1564            pattern = f", PATTERN => {pattern}" if pattern else ""
1565            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1566
1567        ordinality = expression.args.get("ordinality") or ""
1568        if ordinality:
1569            ordinality = f" WITH ORDINALITY{alias}"
1570            alias = ""
1571
1572        when = self.sql(expression, "when")
1573        if when:
1574            table = f"{table} {when}"
1575
1576        return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
1577
1578    def tablesample_sql(
1579        self,
1580        expression: exp.TableSample,
1581        sep: str = " AS ",
1582        tablesample_keyword: t.Optional[str] = None,
1583    ) -> str:
1584        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1585            table = expression.this.copy()
1586            table.set("alias", None)
1587            this = self.sql(table)
1588            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1589        else:
1590            this = self.sql(expression, "this")
1591            alias = ""
1592
1593        method = self.sql(expression, "method")
1594        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1595        numerator = self.sql(expression, "bucket_numerator")
1596        denominator = self.sql(expression, "bucket_denominator")
1597        field = self.sql(expression, "bucket_field")
1598        field = f" ON {field}" if field else ""
1599        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1600        seed = self.sql(expression, "seed")
1601        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1602
1603        size = self.sql(expression, "size")
1604        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1605            size = f"{size} ROWS"
1606
1607        percent = self.sql(expression, "percent")
1608        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1609            percent = f"{percent} PERCENT"
1610
1611        expr = f"{bucket}{percent}{size}"
1612        if self.TABLESAMPLE_REQUIRES_PARENS:
1613            expr = f"({expr})"
1614
1615        return (
1616            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1617        )
1618
1619    def pivot_sql(self, expression: exp.Pivot) -> str:
1620        expressions = self.expressions(expression, flat=True)
1621
1622        if expression.this:
1623            this = self.sql(expression, "this")
1624            if not expressions:
1625                return f"UNPIVOT {this}"
1626
1627            on = f"{self.seg('ON')} {expressions}"
1628            using = self.expressions(expression, key="using", flat=True)
1629            using = f"{self.seg('USING')} {using}" if using else ""
1630            group = self.sql(expression, "group")
1631            return f"PIVOT {this}{on}{using}{group}"
1632
1633        alias = self.sql(expression, "alias")
1634        alias = f" AS {alias}" if alias else ""
1635        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1636        field = self.sql(expression, "field")
1637        include_nulls = expression.args.get("include_nulls")
1638        if include_nulls is not None:
1639            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1640        else:
1641            nulls = ""
1642        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1643
1644    def version_sql(self, expression: exp.Version) -> str:
1645        this = f"FOR {expression.name}"
1646        kind = expression.text("kind")
1647        expr = self.sql(expression, "expression")
1648        return f"{this} {kind} {expr}"
1649
1650    def tuple_sql(self, expression: exp.Tuple) -> str:
1651        return f"({self.expressions(expression, flat=True)})"
1652
1653    def update_sql(self, expression: exp.Update) -> str:
1654        this = self.sql(expression, "this")
1655        set_sql = self.expressions(expression, flat=True)
1656        from_sql = self.sql(expression, "from")
1657        where_sql = self.sql(expression, "where")
1658        returning = self.sql(expression, "returning")
1659        order = self.sql(expression, "order")
1660        limit = self.sql(expression, "limit")
1661        if self.RETURNING_END:
1662            expression_sql = f"{from_sql}{where_sql}{returning}"
1663        else:
1664            expression_sql = f"{returning}{from_sql}{where_sql}"
1665        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1666        return self.prepend_ctes(expression, sql)
1667
1668    def values_sql(self, expression: exp.Values) -> str:
1669        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1670        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1671            args = self.expressions(expression)
1672            alias = self.sql(expression, "alias")
1673            values = f"VALUES{self.seg('')}{args}"
1674            values = (
1675                f"({values})"
1676                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1677                else values
1678            )
1679            return f"{values} AS {alias}" if alias else values
1680
1681        # Converts `VALUES...` expression into a series of select unions.
1682        alias_node = expression.args.get("alias")
1683        column_names = alias_node and alias_node.columns
1684
1685        selects: t.List[exp.Subqueryable] = []
1686
1687        for i, tup in enumerate(expression.expressions):
1688            row = tup.expressions
1689
1690            if i == 0 and column_names:
1691                row = [
1692                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1693                ]
1694
1695            selects.append(exp.Select(expressions=row))
1696
1697        if self.pretty:
1698            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1699            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1700            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1701            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1702            return self.subquery_sql(
1703                subqueryable.subquery(alias_node and alias_node.this, copy=False)
1704            )
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        return csv(
2075            *sqls,
2076            *[self.sql(join) for join in expression.args.get("joins") or []],
2077            self.sql(expression, "connect"),
2078            self.sql(expression, "match"),
2079            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2080            self.sql(expression, "where"),
2081            self.sql(expression, "group"),
2082            self.sql(expression, "having"),
2083            *self.after_having_modifiers(expression),
2084            self.sql(expression, "order"),
2085            *offset_limit_modifiers,
2086            *self.after_limit_modifiers(expression),
2087            sep="",
2088        )
2089
2090    def offset_limit_modifiers(
2091        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2092    ) -> t.List[str]:
2093        return [
2094            self.sql(expression, "offset") if fetch else self.sql(limit),
2095            self.sql(limit) if fetch else self.sql(expression, "offset"),
2096        ]
2097
2098    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
2099        return [
2100            self.sql(expression, "qualify"),
2101            (
2102                self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
2103                if expression.args.get("windows")
2104                else ""
2105            ),
2106            self.sql(expression, "distribute"),
2107            self.sql(expression, "sort"),
2108            self.sql(expression, "cluster"),
2109        ]
2110
2111    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2112        locks = self.expressions(expression, key="locks", sep=" ")
2113        locks = f" {locks}" if locks else ""
2114        return [locks, self.sql(expression, "sample")]
2115
2116    def select_sql(self, expression: exp.Select) -> str:
2117        into = expression.args.get("into")
2118        if not self.SUPPORTS_SELECT_INTO and into:
2119            into.pop()
2120
2121        hint = self.sql(expression, "hint")
2122        distinct = self.sql(expression, "distinct")
2123        distinct = f" {distinct}" if distinct else ""
2124        kind = self.sql(expression, "kind")
2125        limit = expression.args.get("limit")
2126        top = (
2127            self.limit_sql(limit, top=True)
2128            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2129            else ""
2130        )
2131
2132        expressions = self.expressions(expression)
2133
2134        if kind:
2135            if kind in self.SELECT_KINDS:
2136                kind = f" AS {kind}"
2137            else:
2138                if kind == "STRUCT":
2139                    expressions = self.expressions(
2140                        sqls=[
2141                            self.sql(
2142                                exp.Struct(
2143                                    expressions=[
2144                                        exp.column(e.output_name).eq(
2145                                            e.this if isinstance(e, exp.Alias) else e
2146                                        )
2147                                        for e in expression.expressions
2148                                    ]
2149                                )
2150                            )
2151                        ]
2152                    )
2153                kind = ""
2154
2155        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2156        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2157        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2158        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2159        sql = self.query_modifiers(
2160            expression,
2161            f"SELECT{top_distinct}{kind}{expressions}",
2162            self.sql(expression, "into", comment=False),
2163            self.sql(expression, "from", comment=False),
2164        )
2165
2166        sql = self.prepend_ctes(expression, sql)
2167
2168        if not self.SUPPORTS_SELECT_INTO and into:
2169            if into.args.get("temporary"):
2170                table_kind = " TEMPORARY"
2171            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2172                table_kind = " UNLOGGED"
2173            else:
2174                table_kind = ""
2175            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2176
2177        return sql
2178
2179    def schema_sql(self, expression: exp.Schema) -> str:
2180        this = self.sql(expression, "this")
2181        sql = self.schema_columns_sql(expression)
2182        return f"{this} {sql}" if this and sql else this or sql
2183
2184    def schema_columns_sql(self, expression: exp.Schema) -> str:
2185        if expression.expressions:
2186            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2187        return ""
2188
2189    def star_sql(self, expression: exp.Star) -> str:
2190        except_ = self.expressions(expression, key="except", flat=True)
2191        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2192        replace = self.expressions(expression, key="replace", flat=True)
2193        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2194        return f"*{except_}{replace}"
2195
2196    def parameter_sql(self, expression: exp.Parameter) -> str:
2197        this = self.sql(expression, "this")
2198        return f"{self.PARAMETER_TOKEN}{this}"
2199
2200    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2201        this = self.sql(expression, "this")
2202        kind = expression.text("kind")
2203        if kind:
2204            kind = f"{kind}."
2205        return f"@@{kind}{this}"
2206
2207    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2208        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
2209
2210    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2211        alias = self.sql(expression, "alias")
2212        alias = f"{sep}{alias}" if alias else ""
2213
2214        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2215        pivots = f" {pivots}" if pivots else ""
2216
2217        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2218        return self.prepend_ctes(expression, sql)
2219
2220    def qualify_sql(self, expression: exp.Qualify) -> str:
2221        this = self.indent(self.sql(expression, "this"))
2222        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
2223
2224    def union_sql(self, expression: exp.Union) -> str:
2225        return self.prepend_ctes(
2226            expression,
2227            self.set_operation(expression, self.union_op(expression)),
2228        )
2229
2230    def union_op(self, expression: exp.Union) -> str:
2231        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2232        kind = kind if expression.args.get("distinct") else " ALL"
2233        by_name = " BY NAME" if expression.args.get("by_name") else ""
2234        return f"UNION{kind}{by_name}"
2235
2236    def unnest_sql(self, expression: exp.Unnest) -> str:
2237        args = self.expressions(expression, flat=True)
2238
2239        alias = expression.args.get("alias")
2240        offset = expression.args.get("offset")
2241
2242        if self.UNNEST_WITH_ORDINALITY:
2243            if alias and isinstance(offset, exp.Expression):
2244                alias.append("columns", offset)
2245
2246        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2247            columns = alias.columns
2248            alias = self.sql(columns[0]) if columns else ""
2249        else:
2250            alias = self.sql(alias)
2251
2252        alias = f" AS {alias}" if alias else alias
2253        if self.UNNEST_WITH_ORDINALITY:
2254            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2255        else:
2256            if isinstance(offset, exp.Expression):
2257                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2258            elif offset:
2259                suffix = f"{alias} WITH OFFSET"
2260            else:
2261                suffix = alias
2262
2263        return f"UNNEST({args}){suffix}"
2264
2265    def where_sql(self, expression: exp.Where) -> str:
2266        this = self.indent(self.sql(expression, "this"))
2267        return f"{self.seg('WHERE')}{self.sep()}{this}"
2268
2269    def window_sql(self, expression: exp.Window) -> str:
2270        this = self.sql(expression, "this")
2271        partition = self.partition_by_sql(expression)
2272        order = expression.args.get("order")
2273        order = self.order_sql(order, flat=True) if order else ""
2274        spec = self.sql(expression, "spec")
2275        alias = self.sql(expression, "alias")
2276        over = self.sql(expression, "over") or "OVER"
2277
2278        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2279
2280        first = expression.args.get("first")
2281        if first is None:
2282            first = ""
2283        else:
2284            first = "FIRST" if first else "LAST"
2285
2286        if not partition and not order and not spec and alias:
2287            return f"{this} {alias}"
2288
2289        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2290        return f"{this} ({args})"
2291
2292    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2293        partition = self.expressions(expression, key="partition_by", flat=True)
2294        return f"PARTITION BY {partition}" if partition else ""
2295
2296    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2297        kind = self.sql(expression, "kind")
2298        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2299        end = (
2300            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2301            or "CURRENT ROW"
2302        )
2303        return f"{kind} BETWEEN {start} AND {end}"
2304
2305    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2306        this = self.sql(expression, "this")
2307        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2308        return f"{this} WITHIN GROUP ({expression_sql})"
2309
2310    def between_sql(self, expression: exp.Between) -> str:
2311        this = self.sql(expression, "this")
2312        low = self.sql(expression, "low")
2313        high = self.sql(expression, "high")
2314        return f"{this} BETWEEN {low} AND {high}"
2315
2316    def bracket_sql(self, expression: exp.Bracket) -> str:
2317        expressions = apply_index_offset(
2318            expression.this,
2319            expression.expressions,
2320            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2321        )
2322        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2323        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2324
2325    def all_sql(self, expression: exp.All) -> str:
2326        return f"ALL {self.wrap(expression)}"
2327
2328    def any_sql(self, expression: exp.Any) -> str:
2329        this = self.sql(expression, "this")
2330        if isinstance(expression.this, exp.Subqueryable):
2331            this = self.wrap(this)
2332        return f"ANY {this}"
2333
2334    def exists_sql(self, expression: exp.Exists) -> str:
2335        return f"EXISTS{self.wrap(expression)}"
2336
2337    def case_sql(self, expression: exp.Case) -> str:
2338        this = self.sql(expression, "this")
2339        statements = [f"CASE {this}" if this else "CASE"]
2340
2341        for e in expression.args["ifs"]:
2342            statements.append(f"WHEN {self.sql(e, 'this')}")
2343            statements.append(f"THEN {self.sql(e, 'true')}")
2344
2345        default = self.sql(expression, "default")
2346
2347        if default:
2348            statements.append(f"ELSE {default}")
2349
2350        statements.append("END")
2351
2352        if self.pretty and self.text_width(statements) > self.max_text_width:
2353            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2354
2355        return " ".join(statements)
2356
2357    def constraint_sql(self, expression: exp.Constraint) -> str:
2358        this = self.sql(expression, "this")
2359        expressions = self.expressions(expression, flat=True)
2360        return f"CONSTRAINT {this} {expressions}"
2361
2362    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2363        order = expression.args.get("order")
2364        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2365        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
2366
2367    def extract_sql(self, expression: exp.Extract) -> str:
2368        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2369        expression_sql = self.sql(expression, "expression")
2370        return f"EXTRACT({this} FROM {expression_sql})"
2371
2372    def trim_sql(self, expression: exp.Trim) -> str:
2373        trim_type = self.sql(expression, "position")
2374
2375        if trim_type == "LEADING":
2376            return self.func("LTRIM", expression.this)
2377        elif trim_type == "TRAILING":
2378            return self.func("RTRIM", expression.this)
2379        else:
2380            return self.func("TRIM", expression.this, expression.expression)
2381
2382    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2383        args = expression.expressions
2384        if isinstance(expression, exp.ConcatWs):
2385            args = args[1:]  # Skip the delimiter
2386
2387        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2388            args = [exp.cast(e, "text") for e in args]
2389
2390        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2391            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2392
2393        return args
2394
2395    def concat_sql(self, expression: exp.Concat) -> str:
2396        expressions = self.convert_concat_args(expression)
2397
2398        # Some dialects don't allow a single-argument CONCAT call
2399        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2400            return self.sql(expressions[0])
2401
2402        return self.func("CONCAT", *expressions)
2403
2404    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2405        return self.func(
2406            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2407        )
2408
2409    def check_sql(self, expression: exp.Check) -> str:
2410        this = self.sql(expression, key="this")
2411        return f"CHECK ({this})"
2412
2413    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2414        expressions = self.expressions(expression, flat=True)
2415        reference = self.sql(expression, "reference")
2416        reference = f" {reference}" if reference else ""
2417        delete = self.sql(expression, "delete")
2418        delete = f" ON DELETE {delete}" if delete else ""
2419        update = self.sql(expression, "update")
2420        update = f" ON UPDATE {update}" if update else ""
2421        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2422
2423    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2424        expressions = self.expressions(expression, flat=True)
2425        options = self.expressions(expression, key="options", flat=True, sep=" ")
2426        options = f" {options}" if options else ""
2427        return f"PRIMARY KEY ({expressions}){options}"
2428
2429    def if_sql(self, expression: exp.If) -> str:
2430        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
2431
2432    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2433        modifier = expression.args.get("modifier")
2434        modifier = f" {modifier}" if modifier else ""
2435        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
2436
2437    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2438        return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}"
2439
2440    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2441        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2442        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
2443
2444    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2445        if isinstance(expression, exp.JSONPathPart):
2446            transform = self.TRANSFORMS.get(expression.__class__)
2447            if not callable(transform):
2448                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2449                return ""
2450
2451            return transform(self, expression)
2452
2453        if isinstance(expression, int):
2454            return str(expression)
2455
2456        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2457            escaped = expression.replace("'", "\\'")
2458            escaped = f"\\'{expression}\\'"
2459        else:
2460            escaped = expression.replace('"', '\\"')
2461            escaped = f'"{escaped}"'
2462
2463        return escaped
2464
2465    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2466        return f"{self.sql(expression, 'this')} FORMAT JSON"
2467
2468    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2469        null_handling = expression.args.get("null_handling")
2470        null_handling = f" {null_handling}" if null_handling else ""
2471
2472        unique_keys = expression.args.get("unique_keys")
2473        if unique_keys is not None:
2474            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2475        else:
2476            unique_keys = ""
2477
2478        return_type = self.sql(expression, "return_type")
2479        return_type = f" RETURNING {return_type}" if return_type else ""
2480        encoding = self.sql(expression, "encoding")
2481        encoding = f" ENCODING {encoding}" if encoding else ""
2482
2483        return self.func(
2484            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2485            *expression.expressions,
2486            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2487        )
2488
2489    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2490        return self.jsonobject_sql(expression)
2491
2492    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2493        null_handling = expression.args.get("null_handling")
2494        null_handling = f" {null_handling}" if null_handling else ""
2495        return_type = self.sql(expression, "return_type")
2496        return_type = f" RETURNING {return_type}" if return_type else ""
2497        strict = " STRICT" if expression.args.get("strict") else ""
2498        return self.func(
2499            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2500        )
2501
2502    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2503        this = self.sql(expression, "this")
2504        order = self.sql(expression, "order")
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_ARRAYAGG",
2512            this,
2513            suffix=f"{order}{null_handling}{return_type}{strict})",
2514        )
2515
2516    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2517        path = self.sql(expression, "path")
2518        path = f" PATH {path}" if path else ""
2519        nested_schema = self.sql(expression, "nested_schema")
2520
2521        if nested_schema:
2522            return f"NESTED{path} {nested_schema}"
2523
2524        this = self.sql(expression, "this")
2525        kind = self.sql(expression, "kind")
2526        kind = f" {kind}" if kind else ""
2527        return f"{this}{kind}{path}"
2528
2529    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2530        return self.func("COLUMNS", *expression.expressions)
2531
2532    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2533        this = self.sql(expression, "this")
2534        path = self.sql(expression, "path")
2535        path = f", {path}" if path else ""
2536        error_handling = expression.args.get("error_handling")
2537        error_handling = f" {error_handling}" if error_handling else ""
2538        empty_handling = expression.args.get("empty_handling")
2539        empty_handling = f" {empty_handling}" if empty_handling else ""
2540        schema = self.sql(expression, "schema")
2541        return self.func(
2542            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2543        )
2544
2545    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2546        this = self.sql(expression, "this")
2547        kind = self.sql(expression, "kind")
2548        path = self.sql(expression, "path")
2549        path = f" {path}" if path else ""
2550        as_json = " AS JSON" if expression.args.get("as_json") else ""
2551        return f"{this} {kind}{path}{as_json}"
2552
2553    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2554        this = self.sql(expression, "this")
2555        path = self.sql(expression, "path")
2556        path = f", {path}" if path else ""
2557        expressions = self.expressions(expression)
2558        with_ = (
2559            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2560            if expressions
2561            else ""
2562        )
2563        return f"OPENJSON({this}{path}){with_}"
2564
2565    def in_sql(self, expression: exp.In) -> str:
2566        query = expression.args.get("query")
2567        unnest = expression.args.get("unnest")
2568        field = expression.args.get("field")
2569        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2570
2571        if query:
2572            in_sql = self.wrap(query)
2573        elif unnest:
2574            in_sql = self.in_unnest_op(unnest)
2575        elif field:
2576            in_sql = self.sql(field)
2577        else:
2578            in_sql = f"({self.expressions(expression, flat=True)})"
2579
2580        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2581
2582    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2583        return f"(SELECT {self.sql(unnest)})"
2584
2585    def interval_sql(self, expression: exp.Interval) -> str:
2586        unit = self.sql(expression, "unit")
2587        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2588            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2589        unit = f" {unit}" if unit else ""
2590
2591        if self.SINGLE_STRING_INTERVAL:
2592            this = expression.this.name if expression.this else ""
2593            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2594
2595        this = self.sql(expression, "this")
2596        if this:
2597            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2598            this = f" {this}" if unwrapped else f" ({this})"
2599
2600        return f"INTERVAL{this}{unit}"
2601
2602    def return_sql(self, expression: exp.Return) -> str:
2603        return f"RETURN {self.sql(expression, 'this')}"
2604
2605    def reference_sql(self, expression: exp.Reference) -> str:
2606        this = self.sql(expression, "this")
2607        expressions = self.expressions(expression, flat=True)
2608        expressions = f"({expressions})" if expressions else ""
2609        options = self.expressions(expression, key="options", flat=True, sep=" ")
2610        options = f" {options}" if options else ""
2611        return f"REFERENCES {this}{expressions}{options}"
2612
2613    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2614        return self.func(expression.name, *expression.expressions)
2615
2616    def paren_sql(self, expression: exp.Paren) -> str:
2617        if isinstance(expression.unnest(), exp.Select):
2618            sql = self.wrap(expression)
2619        else:
2620            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2621            sql = f"({sql}{self.seg(')', sep='')}"
2622
2623        return self.prepend_ctes(expression, sql)
2624
2625    def neg_sql(self, expression: exp.Neg) -> str:
2626        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2627        this_sql = self.sql(expression, "this")
2628        sep = " " if this_sql[0] == "-" else ""
2629        return f"-{sep}{this_sql}"
2630
2631    def not_sql(self, expression: exp.Not) -> str:
2632        return f"NOT {self.sql(expression, 'this')}"
2633
2634    def alias_sql(self, expression: exp.Alias) -> str:
2635        alias = self.sql(expression, "alias")
2636        alias = f" AS {alias}" if alias else ""
2637        return f"{self.sql(expression, 'this')}{alias}"
2638
2639    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2640        alias = expression.args["alias"]
2641        identifier_alias = isinstance(alias, exp.Identifier)
2642
2643        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2644            alias.replace(exp.Literal.string(alias.output_name))
2645        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2646            alias.replace(exp.to_identifier(alias.output_name))
2647
2648        return self.alias_sql(expression)
2649
2650    def aliases_sql(self, expression: exp.Aliases) -> str:
2651        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
2652
2653    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2654        this = self.sql(expression, "this")
2655        index = self.sql(expression, "expression")
2656        return f"{this} AT {index}"
2657
2658    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2659        this = self.sql(expression, "this")
2660        zone = self.sql(expression, "zone")
2661        return f"{this} AT TIME ZONE {zone}"
2662
2663    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2664        this = self.sql(expression, "this")
2665        zone = self.sql(expression, "zone")
2666        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
2667
2668    def add_sql(self, expression: exp.Add) -> str:
2669        return self.binary(expression, "+")
2670
2671    def and_sql(self, expression: exp.And) -> str:
2672        return self.connector_sql(expression, "AND")
2673
2674    def xor_sql(self, expression: exp.Xor) -> str:
2675        return self.connector_sql(expression, "XOR")
2676
2677    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2678        if not self.pretty:
2679            return self.binary(expression, op)
2680
2681        sqls = tuple(
2682            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2683            for i, e in enumerate(expression.flatten(unnest=False))
2684        )
2685
2686        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2687        return f"{sep}{op} ".join(sqls)
2688
2689    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2690        return self.binary(expression, "&")
2691
2692    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2693        return self.binary(expression, "<<")
2694
2695    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2696        return f"~{self.sql(expression, 'this')}"
2697
2698    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2699        return self.binary(expression, "|")
2700
2701    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2702        return self.binary(expression, ">>")
2703
2704    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2705        return self.binary(expression, "^")
2706
2707    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2708        format_sql = self.sql(expression, "format")
2709        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2710        to_sql = self.sql(expression, "to")
2711        to_sql = f" {to_sql}" if to_sql else ""
2712        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2713
2714    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2715        zone = self.sql(expression, "this")
2716        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
2717
2718    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2719        return self.func("CURRENT_TIMESTAMP", expression.this)
2720
2721    def collate_sql(self, expression: exp.Collate) -> str:
2722        if self.COLLATE_IS_FUNC:
2723            return self.function_fallback_sql(expression)
2724        return self.binary(expression, "COLLATE")
2725
2726    def command_sql(self, expression: exp.Command) -> str:
2727        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
2728
2729    def comment_sql(self, expression: exp.Comment) -> str:
2730        this = self.sql(expression, "this")
2731        kind = expression.args["kind"]
2732        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2733        expression_sql = self.sql(expression, "expression")
2734        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2735
2736    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2737        this = self.sql(expression, "this")
2738        delete = " DELETE" if expression.args.get("delete") else ""
2739        recompress = self.sql(expression, "recompress")
2740        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2741        to_disk = self.sql(expression, "to_disk")
2742        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2743        to_volume = self.sql(expression, "to_volume")
2744        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2745        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2746
2747    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2748        where = self.sql(expression, "where")
2749        group = self.sql(expression, "group")
2750        aggregates = self.expressions(expression, key="aggregates")
2751        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2752
2753        if not (where or group or aggregates) and len(expression.expressions) == 1:
2754            return f"TTL {self.expressions(expression, flat=True)}"
2755
2756        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2757
2758    def transaction_sql(self, expression: exp.Transaction) -> str:
2759        return "BEGIN"
2760
2761    def commit_sql(self, expression: exp.Commit) -> str:
2762        chain = expression.args.get("chain")
2763        if chain is not None:
2764            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2765
2766        return f"COMMIT{chain or ''}"
2767
2768    def rollback_sql(self, expression: exp.Rollback) -> str:
2769        savepoint = expression.args.get("savepoint")
2770        savepoint = f" TO {savepoint}" if savepoint else ""
2771        return f"ROLLBACK{savepoint}"
2772
2773    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2774        this = self.sql(expression, "this")
2775
2776        dtype = self.sql(expression, "dtype")
2777        if dtype:
2778            collate = self.sql(expression, "collate")
2779            collate = f" COLLATE {collate}" if collate else ""
2780            using = self.sql(expression, "using")
2781            using = f" USING {using}" if using else ""
2782            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2783
2784        default = self.sql(expression, "default")
2785        if default:
2786            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2787
2788        comment = self.sql(expression, "comment")
2789        if comment:
2790            return f"ALTER COLUMN {this} COMMENT {comment}"
2791
2792        if not expression.args.get("drop"):
2793            self.unsupported("Unsupported ALTER COLUMN syntax")
2794
2795        return f"ALTER COLUMN {this} DROP DEFAULT"
2796
2797    def renametable_sql(self, expression: exp.RenameTable) -> str:
2798        if not self.RENAME_TABLE_WITH_DB:
2799            # Remove db from tables
2800            expression = expression.transform(
2801                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2802            )
2803        this = self.sql(expression, "this")
2804        return f"RENAME TO {this}"
2805
2806    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2807        exists = " IF EXISTS" if expression.args.get("exists") else ""
2808        old_column = self.sql(expression, "this")
2809        new_column = self.sql(expression, "to")
2810        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
2811
2812    def altertable_sql(self, expression: exp.AlterTable) -> str:
2813        actions = expression.args["actions"]
2814
2815        if isinstance(actions[0], exp.ColumnDef):
2816            actions = self.add_column_sql(expression)
2817        elif isinstance(actions[0], exp.Schema):
2818            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2819        elif isinstance(actions[0], exp.Delete):
2820            actions = self.expressions(expression, key="actions", flat=True)
2821        else:
2822            actions = self.expressions(expression, key="actions", flat=True)
2823
2824        exists = " IF EXISTS" if expression.args.get("exists") else ""
2825        only = " ONLY" if expression.args.get("only") else ""
2826        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
2827
2828    def add_column_sql(self, expression: exp.AlterTable) -> str:
2829        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2830            return self.expressions(
2831                expression,
2832                key="actions",
2833                prefix="ADD COLUMN ",
2834            )
2835        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
2836
2837    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2838        expressions = self.expressions(expression)
2839        exists = " IF EXISTS " if expression.args.get("exists") else " "
2840        return f"DROP{exists}{expressions}"
2841
2842    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2843        return f"ADD {self.expressions(expression)}"
2844
2845    def distinct_sql(self, expression: exp.Distinct) -> str:
2846        this = self.expressions(expression, flat=True)
2847
2848        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2849            case = exp.case()
2850            for arg in expression.expressions:
2851                case = case.when(arg.is_(exp.null()), exp.null())
2852            this = self.sql(case.else_(f"({this})"))
2853
2854        this = f" {this}" if this else ""
2855
2856        on = self.sql(expression, "on")
2857        on = f" ON {on}" if on else ""
2858        return f"DISTINCT{this}{on}"
2859
2860    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2861        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
2862
2863    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2864        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
2865
2866    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2867        this_sql = self.sql(expression, "this")
2868        expression_sql = self.sql(expression, "expression")
2869        kind = "MAX" if expression.args.get("max") else "MIN"
2870        return f"{this_sql} HAVING {kind} {expression_sql}"
2871
2872    def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str:
2873        if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"):
2874            # The first modifier here will be the one closest to the AggFunc's arg
2875            mods = sorted(
2876                expression.find_all(exp.HavingMax, exp.Order, exp.Limit),
2877                key=lambda x: 0
2878                if isinstance(x, exp.HavingMax)
2879                else (1 if isinstance(x, exp.Order) else 2),
2880            )
2881
2882            if mods:
2883                mod = mods[0]
2884                this = expression.__class__(this=mod.this.copy())
2885                this.meta["inline"] = True
2886                mod.this.replace(this)
2887                return self.sql(expression.this)
2888
2889            agg_func = expression.find(exp.AggFunc)
2890
2891            if agg_func:
2892                return self.sql(agg_func)[:-1] + f" {text})"
2893
2894        return f"{self.sql(expression, 'this')} {text}"
2895
2896    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2897        return self.sql(
2898            exp.Cast(
2899                this=exp.Div(this=expression.this, expression=expression.expression),
2900                to=exp.DataType(this=exp.DataType.Type.INT),
2901            )
2902        )
2903
2904    def dpipe_sql(self, expression: exp.DPipe) -> str:
2905        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2906            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2907        return self.binary(expression, "||")
2908
2909    def div_sql(self, expression: exp.Div) -> str:
2910        l, r = expression.left, expression.right
2911
2912        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2913            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2914
2915        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2916            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
2917                *exp.DataType.FLOAT_TYPES
2918            ):
2919                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2920
2921        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2922            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2923                return self.sql(
2924                    exp.cast(
2925                        l / r,
2926                        to=exp.DataType.Type.BIGINT,
2927                    )
2928                )
2929
2930        return self.binary(expression, "/")
2931
2932    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2933        return self.binary(expression, "OVERLAPS")
2934
2935    def distance_sql(self, expression: exp.Distance) -> str:
2936        return self.binary(expression, "<->")
2937
2938    def dot_sql(self, expression: exp.Dot) -> str:
2939        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
2940
2941    def eq_sql(self, expression: exp.EQ) -> str:
2942        return self.binary(expression, "=")
2943
2944    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
2945        return self.binary(expression, ":=")
2946
2947    def escape_sql(self, expression: exp.Escape) -> str:
2948        return self.binary(expression, "ESCAPE")
2949
2950    def glob_sql(self, expression: exp.Glob) -> str:
2951        return self.binary(expression, "GLOB")
2952
2953    def gt_sql(self, expression: exp.GT) -> str:
2954        return self.binary(expression, ">")
2955
2956    def gte_sql(self, expression: exp.GTE) -> str:
2957        return self.binary(expression, ">=")
2958
2959    def ilike_sql(self, expression: exp.ILike) -> str:
2960        return self.binary(expression, "ILIKE")
2961
2962    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2963        return self.binary(expression, "ILIKE ANY")
2964
2965    def is_sql(self, expression: exp.Is) -> str:
2966        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2967            return self.sql(
2968                expression.this if expression.expression.this else exp.not_(expression.this)
2969            )
2970        return self.binary(expression, "IS")
2971
2972    def like_sql(self, expression: exp.Like) -> str:
2973        return self.binary(expression, "LIKE")
2974
2975    def likeany_sql(self, expression: exp.LikeAny) -> str:
2976        return self.binary(expression, "LIKE ANY")
2977
2978    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2979        return self.binary(expression, "SIMILAR TO")
2980
2981    def lt_sql(self, expression: exp.LT) -> str:
2982        return self.binary(expression, "<")
2983
2984    def lte_sql(self, expression: exp.LTE) -> str:
2985        return self.binary(expression, "<=")
2986
2987    def mod_sql(self, expression: exp.Mod) -> str:
2988        return self.binary(expression, "%")
2989
2990    def mul_sql(self, expression: exp.Mul) -> str:
2991        return self.binary(expression, "*")
2992
2993    def neq_sql(self, expression: exp.NEQ) -> str:
2994        return self.binary(expression, "<>")
2995
2996    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
2997        return self.binary(expression, "IS NOT DISTINCT FROM")
2998
2999    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3000        return self.binary(expression, "IS DISTINCT FROM")
3001
3002    def or_sql(self, expression: exp.Or) -> str:
3003        return self.connector_sql(expression, "OR")
3004
3005    def slice_sql(self, expression: exp.Slice) -> str:
3006        return self.binary(expression, ":")
3007
3008    def sub_sql(self, expression: exp.Sub) -> str:
3009        return self.binary(expression, "-")
3010
3011    def trycast_sql(self, expression: exp.TryCast) -> str:
3012        return self.cast_sql(expression, safe_prefix="TRY_")
3013
3014    def log_sql(self, expression: exp.Log) -> str:
3015        this = expression.this
3016        expr = expression.expression
3017
3018        if not self.dialect.LOG_BASE_FIRST:
3019            this, expr = expr, this
3020
3021        return self.func("LOG", this, expr)
3022
3023    def use_sql(self, expression: exp.Use) -> str:
3024        kind = self.sql(expression, "kind")
3025        kind = f" {kind}" if kind else ""
3026        this = self.sql(expression, "this")
3027        this = f" {this}" if this else ""
3028        return f"USE{kind}{this}"
3029
3030    def binary(self, expression: exp.Binary, op: str) -> str:
3031        op = self.maybe_comment(op, comments=expression.comments)
3032        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
3033
3034    def function_fallback_sql(self, expression: exp.Func) -> str:
3035        args = []
3036
3037        for key in expression.arg_types:
3038            arg_value = expression.args.get(key)
3039
3040            if isinstance(arg_value, list):
3041                for value in arg_value:
3042                    args.append(value)
3043            elif arg_value is not None:
3044                args.append(arg_value)
3045
3046        if self.normalize_functions:
3047            name = expression.sql_name()
3048        else:
3049            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3050
3051        return self.func(name, *args)
3052
3053    def func(
3054        self,
3055        name: str,
3056        *args: t.Optional[exp.Expression | str],
3057        prefix: str = "(",
3058        suffix: str = ")",
3059    ) -> str:
3060        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
3061
3062    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3063        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3064        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3065            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3066        return ", ".join(arg_sqls)
3067
3068    def text_width(self, args: t.Iterable) -> int:
3069        return sum(len(arg) for arg in args)
3070
3071    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3072        return format_time(
3073            self.sql(expression, "format"),
3074            self.dialect.INVERSE_TIME_MAPPING,
3075            self.dialect.INVERSE_TIME_TRIE,
3076        )
3077
3078    def expressions(
3079        self,
3080        expression: t.Optional[exp.Expression] = None,
3081        key: t.Optional[str] = None,
3082        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3083        flat: bool = False,
3084        indent: bool = True,
3085        skip_first: bool = False,
3086        sep: str = ", ",
3087        prefix: str = "",
3088    ) -> str:
3089        expressions = expression.args.get(key or "expressions") if expression else sqls
3090
3091        if not expressions:
3092            return ""
3093
3094        if flat:
3095            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3096
3097        num_sqls = len(expressions)
3098
3099        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3100        pad = " " * self.pad
3101        stripped_sep = sep.strip()
3102
3103        result_sqls = []
3104        for i, e in enumerate(expressions):
3105            sql = self.sql(e, comment=False)
3106            if not sql:
3107                continue
3108
3109            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3110
3111            if self.pretty:
3112                if self.leading_comma:
3113                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3114                else:
3115                    result_sqls.append(
3116                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3117                    )
3118            else:
3119                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3120
3121        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3122        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
3123
3124    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3125        flat = flat or isinstance(expression.parent, exp.Properties)
3126        expressions_sql = self.expressions(expression, flat=flat)
3127        if flat:
3128            return f"{op} {expressions_sql}"
3129        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3130
3131    def naked_property(self, expression: exp.Property) -> str:
3132        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3133        if not property_name:
3134            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3135        return f"{property_name} {self.sql(expression, 'this')}"
3136
3137    def set_operation(self, expression: exp.Union, op: str) -> str:
3138        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
3139        op = self.seg(op)
3140        return self.query_modifiers(
3141            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
3142        )
3143
3144    def tag_sql(self, expression: exp.Tag) -> str:
3145        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
3146
3147    def token_sql(self, token_type: TokenType) -> str:
3148        return self.TOKEN_MAPPING.get(token_type, token_type.name)
3149
3150    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3151        this = self.sql(expression, "this")
3152        expressions = self.no_identify(self.expressions, expression)
3153        expressions = (
3154            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3155        )
3156        return f"{this}{expressions}"
3157
3158    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3159        this = self.sql(expression, "this")
3160        expressions = self.expressions(expression, flat=True)
3161        return f"{this}({expressions})"
3162
3163    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3164        return self.binary(expression, "=>")
3165
3166    def when_sql(self, expression: exp.When) -> str:
3167        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3168        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3169        condition = self.sql(expression, "condition")
3170        condition = f" AND {condition}" if condition else ""
3171
3172        then_expression = expression.args.get("then")
3173        if isinstance(then_expression, exp.Insert):
3174            then = f"INSERT {self.sql(then_expression, 'this')}"
3175            if "expression" in then_expression.args:
3176                then += f" VALUES {self.sql(then_expression, 'expression')}"
3177        elif isinstance(then_expression, exp.Update):
3178            if isinstance(then_expression.args.get("expressions"), exp.Star):
3179                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3180            else:
3181                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3182        else:
3183            then = self.sql(then_expression)
3184        return f"WHEN {matched}{source}{condition} THEN {then}"
3185
3186    def merge_sql(self, expression: exp.Merge) -> str:
3187        table = expression.this
3188        table_alias = ""
3189
3190        hints = table.args.get("hints")
3191        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3192            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3193            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3194
3195        this = self.sql(table)
3196        using = f"USING {self.sql(expression, 'using')}"
3197        on = f"ON {self.sql(expression, 'on')}"
3198        expressions = self.expressions(expression, sep=" ")
3199
3200        return self.prepend_ctes(
3201            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3202        )
3203
3204    def tochar_sql(self, expression: exp.ToChar) -> str:
3205        if expression.args.get("format"):
3206            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3207
3208        return self.sql(exp.cast(expression.this, "text"))
3209
3210    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3211        this = self.sql(expression, "this")
3212        kind = self.sql(expression, "kind")
3213        settings_sql = self.expressions(expression, key="settings", sep=" ")
3214        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3215        return f"{this}({kind}{args})"
3216
3217    def dictrange_sql(self, expression: exp.DictRange) -> str:
3218        this = self.sql(expression, "this")
3219        max = self.sql(expression, "max")
3220        min = self.sql(expression, "min")
3221        return f"{this}(MIN {min} MAX {max})"
3222
3223    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3224        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
3225
3226    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3227        return ""
3228
3229    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3230        expressions = self.expressions(expression, key="expressions", flat=True)
3231        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3232        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3233        buckets = self.sql(expression, "buckets")
3234        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3235
3236    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3237        this = self.sql(expression, "this")
3238        having = self.sql(expression, "having")
3239
3240        if having:
3241            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3242
3243        return self.func("ANY_VALUE", this)
3244
3245    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3246        transform = self.func("TRANSFORM", *expression.expressions)
3247        row_format_before = self.sql(expression, "row_format_before")
3248        row_format_before = f" {row_format_before}" if row_format_before else ""
3249        record_writer = self.sql(expression, "record_writer")
3250        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3251        using = f" USING {self.sql(expression, 'command_script')}"
3252        schema = self.sql(expression, "schema")
3253        schema = f" AS {schema}" if schema else ""
3254        row_format_after = self.sql(expression, "row_format_after")
3255        row_format_after = f" {row_format_after}" if row_format_after else ""
3256        record_reader = self.sql(expression, "record_reader")
3257        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3258        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3259
3260    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3261        key_block_size = self.sql(expression, "key_block_size")
3262        if key_block_size:
3263            return f"KEY_BLOCK_SIZE = {key_block_size}"
3264
3265        using = self.sql(expression, "using")
3266        if using:
3267            return f"USING {using}"
3268
3269        parser = self.sql(expression, "parser")
3270        if parser:
3271            return f"WITH PARSER {parser}"
3272
3273        comment = self.sql(expression, "comment")
3274        if comment:
3275            return f"COMMENT {comment}"
3276
3277        visible = expression.args.get("visible")
3278        if visible is not None:
3279            return "VISIBLE" if visible else "INVISIBLE"
3280
3281        engine_attr = self.sql(expression, "engine_attr")
3282        if engine_attr:
3283            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3284
3285        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3286        if secondary_engine_attr:
3287            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3288
3289        self.unsupported("Unsupported index constraint option.")
3290        return ""
3291
3292    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3293        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3294        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
3295
3296    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3297        kind = self.sql(expression, "kind")
3298        kind = f"{kind} INDEX" if kind else "INDEX"
3299        this = self.sql(expression, "this")
3300        this = f" {this}" if this else ""
3301        index_type = self.sql(expression, "index_type")
3302        index_type = f" USING {index_type}" if index_type else ""
3303        schema = self.sql(expression, "schema")
3304        schema = f" {schema}" if schema else ""
3305        options = self.expressions(expression, key="options", sep=" ")
3306        options = f" {options}" if options else ""
3307        return f"{kind}{this}{index_type}{schema}{options}"
3308
3309    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3310        if self.NVL2_SUPPORTED:
3311            return self.function_fallback_sql(expression)
3312
3313        case = exp.Case().when(
3314            expression.this.is_(exp.null()).not_(copy=False),
3315            expression.args["true"],
3316            copy=False,
3317        )
3318        else_cond = expression.args.get("false")
3319        if else_cond:
3320            case.else_(else_cond, copy=False)
3321
3322        return self.sql(case)
3323
3324    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3325        this = self.sql(expression, "this")
3326        expr = self.sql(expression, "expression")
3327        iterator = self.sql(expression, "iterator")
3328        condition = self.sql(expression, "condition")
3329        condition = f" IF {condition}" if condition else ""
3330        return f"{this} FOR {expr} IN {iterator}{condition}"
3331
3332    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3333        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
3334
3335    def opclass_sql(self, expression: exp.Opclass) -> str:
3336        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
3337
3338    def predict_sql(self, expression: exp.Predict) -> str:
3339        model = self.sql(expression, "this")
3340        model = f"MODEL {model}"
3341        table = self.sql(expression, "expression")
3342        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3343        parameters = self.sql(expression, "params_struct")
3344        return self.func("PREDICT", model, table, parameters or None)
3345
3346    def forin_sql(self, expression: exp.ForIn) -> str:
3347        this = self.sql(expression, "this")
3348        expression_sql = self.sql(expression, "expression")
3349        return f"FOR {this} DO {expression_sql}"
3350
3351    def refresh_sql(self, expression: exp.Refresh) -> str:
3352        this = self.sql(expression, "this")
3353        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3354        return f"REFRESH {table}{this}"
3355
3356    def operator_sql(self, expression: exp.Operator) -> str:
3357        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
3358
3359    def toarray_sql(self, expression: exp.ToArray) -> str:
3360        arg = expression.this
3361        if not arg.type:
3362            from sqlglot.optimizer.annotate_types import annotate_types
3363
3364            arg = annotate_types(arg)
3365
3366        if arg.is_type(exp.DataType.Type.ARRAY):
3367            return self.sql(arg)
3368
3369        cond_for_null = arg.is_(exp.null())
3370        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3371
3372    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3373        this = expression.this
3374        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3375            return self.sql(this)
3376
3377        return self.sql(exp.cast(this, "time"))
3378
3379    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3380        this = expression.this
3381        time_format = self.format_time(expression)
3382
3383        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3384            return self.sql(
3385                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3386            )
3387
3388        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3389            return self.sql(this)
3390
3391        return self.sql(exp.cast(this, "date"))
3392
3393    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3394        return self.sql(
3395            exp.func(
3396                "DATEDIFF",
3397                expression.this,
3398                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3399                "day",
3400            )
3401        )
3402
3403    def lastday_sql(self, expression: exp.LastDay) -> str:
3404        if self.LAST_DAY_SUPPORTS_DATE_PART:
3405            return self.function_fallback_sql(expression)
3406
3407        unit = expression.text("unit")
3408        if unit and unit != "MONTH":
3409            self.unsupported("Date parts are not supported in LAST_DAY.")
3410
3411        return self.func("LAST_DAY", expression.this)
3412
3413    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3414        if self.CAN_IMPLEMENT_ARRAY_ANY:
3415            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3416            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3417            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3418            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3419
3420        from sqlglot.dialects import Dialect
3421
3422        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3423        if self.dialect.__class__ != Dialect:
3424            self.unsupported("ARRAY_ANY is unsupported")
3425
3426        return self.function_fallback_sql(expression)
3427
3428    def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str:
3429        this = expression.this
3430        if isinstance(this, exp.JSONPathWildcard):
3431            this = self.json_path_part(this)
3432            return f".{this}" if this else ""
3433
3434        if exp.SAFE_IDENTIFIER_RE.match(this):
3435            return f".{this}"
3436
3437        this = self.json_path_part(this)
3438        return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}"
3439
3440    def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str:
3441        this = self.json_path_part(expression.this)
3442        return f"[{this}]" if this else ""
3443
3444    def _simplify_unless_literal(self, expression: E) -> E:
3445        if not isinstance(expression, exp.Literal):
3446            from sqlglot.optimizer.simplify import simplify
3447
3448            expression = simplify(expression, dialect=self.dialect)
3449
3450        return expression
3451
3452    def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]:
3453        return [
3454            exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string(""))
3455            for value in values
3456            if value
3457        ]

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)
488    def __init__(
489        self,
490        pretty: t.Optional[bool] = None,
491        identify: str | bool = False,
492        normalize: bool = False,
493        pad: int = 2,
494        indent: int = 2,
495        normalize_functions: t.Optional[str | bool] = None,
496        unsupported_level: ErrorLevel = ErrorLevel.WARN,
497        max_unsupported: int = 3,
498        leading_comma: bool = False,
499        max_text_width: int = 80,
500        comments: bool = True,
501        dialect: DialectType = None,
502    ):
503        import sqlglot
504        from sqlglot.dialects import Dialect
505
506        self.pretty = pretty if pretty is not None else sqlglot.pretty
507        self.identify = identify
508        self.normalize = normalize
509        self.pad = pad
510        self._indent = indent
511        self.unsupported_level = unsupported_level
512        self.max_unsupported = max_unsupported
513        self.leading_comma = leading_comma
514        self.max_text_width = max_text_width
515        self.comments = comments
516        self.dialect = Dialect.get_or_raise(dialect)
517
518        # This is both a Dialect property and a Generator argument, so we prioritize the latter
519        self.normalize_functions = (
520            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
521        )
522
523        self.unsupported_messages: t.List[str] = []
524        self._escaped_quote_end: str = (
525            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
526        )
527        self._escaped_identifier_end: str = (
528            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
529        )
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.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()
KEY_VALUE_DEFINITIONS = (<class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str:
531    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
532        """
533        Generates the SQL string corresponding to the given syntax tree.
534
535        Args:
536            expression: The syntax tree.
537            copy: Whether to copy the expression. The generator performs mutations so
538                it is safer to copy.
539
540        Returns:
541            The SQL string corresponding to `expression`.
542        """
543        if copy:
544            expression = expression.copy()
545
546        expression = self.preprocess(expression)
547
548        self.unsupported_messages = []
549        sql = self.sql(expression).strip()
550
551        if self.pretty:
552            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
553
554        if self.unsupported_level == ErrorLevel.IGNORE:
555            return sql
556
557        if self.unsupported_level == ErrorLevel.WARN:
558            for msg in self.unsupported_messages:
559                logger.warning(msg)
560        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
561            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
562
563        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:
565    def preprocess(self, expression: exp.Expression) -> exp.Expression:
566        """Apply generic preprocessing transformations to a given expression."""
567        if (
568            not expression.parent
569            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
570            and any(node.parent is not expression for node in expression.find_all(exp.With))
571        ):
572            from sqlglot.transforms import move_ctes_to_top_level
573
574            expression = move_ctes_to_top_level(expression)
575
576        if self.ENSURE_BOOLS:
577            from sqlglot.transforms import ensure_bools
578
579            expression = ensure_bools(expression)
580
581        return expression

Apply generic preprocessing transformations to a given expression.

def unsupported(self, message: str) -> None:
583    def unsupported(self, message: str) -> None:
584        if self.unsupported_level == ErrorLevel.IMMEDIATE:
585            raise UnsupportedError(message)
586        self.unsupported_messages.append(message)
def sep(self, sep: str = ' ') -> str:
588    def sep(self, sep: str = " ") -> str:
589        return f"{sep.strip()}\n" if self.pretty else sep
def seg(self, sql: str, sep: str = ' ') -> str:
591    def seg(self, sql: str, sep: str = " ") -> str:
592        return f"{self.sep(sep)}{sql}"
def pad_comment(self, comment: str) -> str:
594    def pad_comment(self, comment: str) -> str:
595        comment = " " + comment if comment[0].strip() else comment
596        comment = comment + " " if comment[-1].strip() else comment
597        return comment
def maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None) -> str:
599    def maybe_comment(
600        self,
601        sql: str,
602        expression: t.Optional[exp.Expression] = None,
603        comments: t.Optional[t.List[str]] = None,
604    ) -> str:
605        comments = (
606            ((expression and expression.comments) if comments is None else comments)  # type: ignore
607            if self.comments
608            else None
609        )
610
611        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
612            return sql
613
614        comments_sql = " ".join(
615            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
616        )
617
618        if not comments_sql:
619            return sql
620
621        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
622            return (
623                f"{self.sep()}{comments_sql}{sql}"
624                if sql[0].isspace()
625                else f"{comments_sql}{self.sep()}{sql}"
626            )
627
628        return f"{sql} {comments_sql}"
def wrap(self, expression: sqlglot.expressions.Expression | str) -> str:
630    def wrap(self, expression: exp.Expression | str) -> str:
631        this_sql = self.indent(
632            (
633                self.sql(expression)
634                if isinstance(expression, (exp.Select, exp.Union))
635                else self.sql(expression, "this")
636            ),
637            level=1,
638            pad=0,
639        )
640        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def no_identify(self, func: Callable[..., str], *args, **kwargs) -> str:
642    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
643        original = self.identify
644        self.identify = False
645        result = func(*args, **kwargs)
646        self.identify = original
647        return result
def normalize_func(self, name: str) -> str:
649    def normalize_func(self, name: str) -> str:
650        if self.normalize_functions == "upper" or self.normalize_functions is True:
651            return name.upper()
652        if self.normalize_functions == "lower":
653            return name.lower()
654        return name
def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
656    def indent(
657        self,
658        sql: str,
659        level: int = 0,
660        pad: t.Optional[int] = None,
661        skip_first: bool = False,
662        skip_last: bool = False,
663    ) -> str:
664        if not self.pretty:
665            return sql
666
667        pad = self.pad if pad is None else pad
668        lines = sql.split("\n")
669
670        return "\n".join(
671            (
672                line
673                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
674                else f"{' ' * (level * self._indent + pad)}{line}"
675            )
676            for i, line in enumerate(lines)
677        )
def sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
679    def sql(
680        self,
681        expression: t.Optional[str | exp.Expression],
682        key: t.Optional[str] = None,
683        comment: bool = True,
684    ) -> str:
685        if not expression:
686            return ""
687
688        if isinstance(expression, str):
689            return expression
690
691        if key:
692            value = expression.args.get(key)
693            if value:
694                return self.sql(value)
695            return ""
696
697        transform = self.TRANSFORMS.get(expression.__class__)
698
699        if callable(transform):
700            sql = transform(self, expression)
701        elif isinstance(expression, exp.Expression):
702            exp_handler_name = f"{expression.key}_sql"
703
704            if hasattr(self, exp_handler_name):
705                sql = getattr(self, exp_handler_name)(expression)
706            elif isinstance(expression, exp.Func):
707                sql = self.function_fallback_sql(expression)
708            elif isinstance(expression, exp.Property):
709                sql = self.property_sql(expression)
710            else:
711                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
712        else:
713            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
714
715        return self.maybe_comment(sql, expression) if self.comments and comment else sql
def uncache_sql(self, expression: sqlglot.expressions.Uncache) -> str:
717    def uncache_sql(self, expression: exp.Uncache) -> str:
718        table = self.sql(expression, "this")
719        exists_sql = " IF EXISTS" if expression.args.get("exists") else ""
720        return f"UNCACHE TABLE{exists_sql} {table}"
def cache_sql(self, expression: sqlglot.expressions.Cache) -> str:
722    def cache_sql(self, expression: exp.Cache) -> str:
723        lazy = " LAZY" if expression.args.get("lazy") else ""
724        table = self.sql(expression, "this")
725        options = expression.args.get("options")
726        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
727        sql = self.sql(expression, "expression")
728        sql = f" AS{self.sep()}{sql}" if sql else ""
729        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
730        return self.prepend_ctes(expression, sql)
def characterset_sql(self, expression: sqlglot.expressions.CharacterSet) -> str:
732    def characterset_sql(self, expression: exp.CharacterSet) -> str:
733        if isinstance(expression.parent, exp.Cast):
734            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
735        default = "DEFAULT " if expression.args.get("default") else ""
736        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
def column_sql(self, expression: sqlglot.expressions.Column) -> str:
738    def column_sql(self, expression: exp.Column) -> str:
739        join_mark = " (+)" if expression.args.get("join_mark") else ""
740
741        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
742            join_mark = ""
743            self.unsupported("Outer join syntax using the (+) operator is not supported.")
744
745        column = ".".join(
746            self.sql(part)
747            for part in (
748                expression.args.get("catalog"),
749                expression.args.get("db"),
750                expression.args.get("table"),
751                expression.args.get("this"),
752            )
753            if part
754        )
755
756        return f"{column}{join_mark}"
def columnposition_sql(self, expression: sqlglot.expressions.ColumnPosition) -> str:
758    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
759        this = self.sql(expression, "this")
760        this = f" {this}" if this else ""
761        position = self.sql(expression, "position")
762        return f"{position}{this}"
def columndef_sql(self, expression: sqlglot.expressions.ColumnDef, sep: str = ' ') -> str:
764    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
765        column = self.sql(expression, "this")
766        kind = self.sql(expression, "kind")
767        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
768        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
769        kind = f"{sep}{kind}" if kind else ""
770        constraints = f" {constraints}" if constraints else ""
771        position = self.sql(expression, "position")
772        position = f" {position}" if position else ""
773
774        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
775            kind = ""
776
777        return f"{exists}{column}{kind}{constraints}{position}"
def columnconstraint_sql(self, expression: sqlglot.expressions.ColumnConstraint) -> str:
779    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
780        this = self.sql(expression, "this")
781        kind_sql = self.sql(expression, "kind").strip()
782        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql
def computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
784    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
785        this = self.sql(expression, "this")
786        if expression.args.get("not_null"):
787            persisted = " PERSISTED NOT NULL"
788        elif expression.args.get("persisted"):
789            persisted = " PERSISTED"
790        else:
791            persisted = ""
792        return f"AS {this}{persisted}"
def autoincrementcolumnconstraint_sql(self, _) -> str:
794    def autoincrementcolumnconstraint_sql(self, _) -> str:
795        return self.token_sql(TokenType.AUTO_INCREMENT)
def compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
797    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
798        if isinstance(expression.this, list):
799            this = self.wrap(self.expressions(expression, key="this", flat=True))
800        else:
801            this = self.sql(expression, "this")
802
803        return f"COMPRESS {this}"
def generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
805    def generatedasidentitycolumnconstraint_sql(
806        self, expression: exp.GeneratedAsIdentityColumnConstraint
807    ) -> str:
808        this = ""
809        if expression.this is not None:
810            on_null = " ON NULL" if expression.args.get("on_null") else ""
811            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
812
813        start = expression.args.get("start")
814        start = f"START WITH {start}" if start else ""
815        increment = expression.args.get("increment")
816        increment = f" INCREMENT BY {increment}" if increment else ""
817        minvalue = expression.args.get("minvalue")
818        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
819        maxvalue = expression.args.get("maxvalue")
820        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
821        cycle = expression.args.get("cycle")
822        cycle_sql = ""
823
824        if cycle is not None:
825            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
826            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
827
828        sequence_opts = ""
829        if start or increment or cycle_sql:
830            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
831            sequence_opts = f" ({sequence_opts.strip()})"
832
833        expr = self.sql(expression, "expression")
834        expr = f"({expr})" if expr else "IDENTITY"
835
836        return f"GENERATED{this} AS {expr}{sequence_opts}"
def generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
838    def generatedasrowcolumnconstraint_sql(
839        self, expression: exp.GeneratedAsRowColumnConstraint
840    ) -> str:
841        start = "START" if expression.args.get("start") else "END"
842        hidden = " HIDDEN" if expression.args.get("hidden") else ""
843        return f"GENERATED ALWAYS AS ROW {start}{hidden}"
def periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
845    def periodforsystemtimeconstraint_sql(
846        self, expression: exp.PeriodForSystemTimeConstraint
847    ) -> str:
848        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"
def notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
850    def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str:
851        return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL"
def transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str:
853    def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str:
854        return f"AS {self.sql(expression, 'this')}"
def primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
856    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
857        desc = expression.args.get("desc")
858        if desc is not None:
859            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
860        return "PRIMARY KEY"
def uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
862    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
863        this = self.sql(expression, "this")
864        this = f" {this}" if this else ""
865        index_type = expression.args.get("index_type")
866        index_type = f" USING {index_type}" if index_type else ""
867        return f"UNIQUE{this}{index_type}"
def createable_sql( self, expression: sqlglot.expressions.Create, locations: DefaultDict) -> str:
869    def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str:
870        return self.sql(expression, "this")
def create_sql(self, expression: sqlglot.expressions.Create) -> str:
872    def create_sql(self, expression: exp.Create) -> str:
873        kind = self.sql(expression, "kind")
874        properties = expression.args.get("properties")
875        properties_locs = self.locate_properties(properties) if properties else defaultdict()
876
877        this = self.createable_sql(expression, properties_locs)
878
879        properties_sql = ""
880        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
881            exp.Properties.Location.POST_WITH
882        ):
883            properties_sql = self.sql(
884                exp.Properties(
885                    expressions=[
886                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
887                        *properties_locs[exp.Properties.Location.POST_WITH],
888                    ]
889                )
890            )
891
892        begin = " BEGIN" if expression.args.get("begin") else ""
893        end = " END" if expression.args.get("end") else ""
894
895        expression_sql = self.sql(expression, "expression")
896        if expression_sql:
897            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
898
899            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
900                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
901                    postalias_props_sql = self.properties(
902                        exp.Properties(
903                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
904                        ),
905                        wrapped=False,
906                    )
907                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
908                else:
909                    expression_sql = f" AS{expression_sql}"
910
911        postindex_props_sql = ""
912        if properties_locs.get(exp.Properties.Location.POST_INDEX):
913            postindex_props_sql = self.properties(
914                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
915                wrapped=False,
916                prefix=" ",
917            )
918
919        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
920        indexes = f" {indexes}" if indexes else ""
921        index_sql = indexes + postindex_props_sql
922
923        replace = " OR REPLACE" if expression.args.get("replace") else ""
924        unique = " UNIQUE" if expression.args.get("unique") else ""
925
926        postcreate_props_sql = ""
927        if properties_locs.get(exp.Properties.Location.POST_CREATE):
928            postcreate_props_sql = self.properties(
929                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
930                sep=" ",
931                prefix=" ",
932                wrapped=False,
933            )
934
935        modifiers = "".join((replace, unique, postcreate_props_sql))
936
937        postexpression_props_sql = ""
938        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
939            postexpression_props_sql = self.properties(
940                exp.Properties(
941                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
942                ),
943                sep=" ",
944                prefix=" ",
945                wrapped=False,
946            )
947
948        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
949        no_schema_binding = (
950            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
951        )
952
953        clone = self.sql(expression, "clone")
954        clone = f" {clone}" if clone else ""
955
956        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
957        return self.prepend_ctes(expression, expression_sql)
def clone_sql(self, expression: sqlglot.expressions.Clone) -> str:
959    def clone_sql(self, expression: exp.Clone) -> str:
960        this = self.sql(expression, "this")
961        shallow = "SHALLOW " if expression.args.get("shallow") else ""
962        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
963        return f"{shallow}{keyword} {this}"
def describe_sql(self, expression: sqlglot.expressions.Describe) -> str:
965    def describe_sql(self, expression: exp.Describe) -> str:
966        extended = " EXTENDED" if expression.args.get("extended") else ""
967        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"
def heredoc_sql(self, expression: sqlglot.expressions.Heredoc) -> str:
969    def heredoc_sql(self, expression: exp.Heredoc) -> str:
970        tag = self.sql(expression, "tag")
971        return f"${tag}${self.sql(expression, 'this')}${tag}$"
def prepend_ctes(self, expression: sqlglot.expressions.Expression, sql: str) -> str:
973    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
974        with_ = self.sql(expression, "with")
975        if with_:
976            sql = f"{with_}{self.sep()}{sql}"
977        return sql
def with_sql(self, expression: sqlglot.expressions.With) -> str:
979    def with_sql(self, expression: exp.With) -> str:
980        sql = self.expressions(expression, flat=True)
981        recursive = (
982            "RECURSIVE "
983            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
984            else ""
985        )
986
987        return f"WITH {recursive}{sql}"
def cte_sql(self, expression: sqlglot.expressions.CTE) -> str:
989    def cte_sql(self, expression: exp.CTE) -> str:
990        alias = self.sql(expression, "alias")
991        return f"{alias} AS {self.wrap(expression)}"
def tablealias_sql(self, expression: sqlglot.expressions.TableAlias) -> str:
 993    def tablealias_sql(self, expression: exp.TableAlias) -> str:
 994        alias = self.sql(expression, "this")
 995        columns = self.expressions(expression, key="columns", flat=True)
 996        columns = f"({columns})" if columns else ""
 997
 998        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
 999            columns = ""
1000            self.unsupported("Named columns are not supported in table alias.")
1001
1002        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
1003            alias = "_t"
1004
1005        return f"{alias}{columns}"
def bitstring_sql(self, expression: sqlglot.expressions.BitString) -> str:
1007    def bitstring_sql(self, expression: exp.BitString) -> str:
1008        this = self.sql(expression, "this")
1009        if self.dialect.BIT_START:
1010            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
1011        return f"{int(this, 2)}"
def hexstring_sql(self, expression: sqlglot.expressions.HexString) -> str:
1013    def hexstring_sql(self, expression: exp.HexString) -> str:
1014        this = self.sql(expression, "this")
1015        if self.dialect.HEX_START:
1016            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
1017        return f"{int(this, 16)}"
def bytestring_sql(self, expression: sqlglot.expressions.ByteString) -> str:
1019    def bytestring_sql(self, expression: exp.ByteString) -> str:
1020        this = self.sql(expression, "this")
1021        if self.dialect.BYTE_START:
1022            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
1023        return this
def unicodestring_sql(self, expression: sqlglot.expressions.UnicodeString) -> str:
1025    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
1026        this = self.sql(expression, "this")
1027        escape = expression.args.get("escape")
1028
1029        if self.dialect.UNICODE_START:
1030            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
1031            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
1032
1033        if escape:
1034            pattern = re.compile(rf"{escape.name}(\d+)")
1035        else:
1036            pattern = ESCAPED_UNICODE_RE
1037
1038        this = pattern.sub(r"\\u\1", this)
1039        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
def rawstring_sql(self, expression: sqlglot.expressions.RawString) -> str:
1041    def rawstring_sql(self, expression: exp.RawString) -> str:
1042        string = self.escape_str(expression.this.replace("\\", "\\\\"))
1043        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"
def datatypeparam_sql(self, expression: sqlglot.expressions.DataTypeParam) -> str:
1045    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
1046        this = self.sql(expression, "this")
1047        specifier = self.sql(expression, "expression")
1048        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
1049        return f"{this}{specifier}"
def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
1051    def datatype_sql(self, expression: exp.DataType) -> str:
1052        type_value = expression.this
1053
1054        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
1055            type_sql = self.sql(expression, "kind")
1056        else:
1057            type_sql = (
1058                self.TYPE_MAPPING.get(type_value, type_value.value)
1059                if isinstance(type_value, exp.DataType.Type)
1060                else type_value
1061            )
1062
1063        nested = ""
1064        interior = self.expressions(expression, flat=True)
1065        values = ""
1066
1067        if interior:
1068            if expression.args.get("nested"):
1069                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
1070                if expression.args.get("values") is not None:
1071                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
1072                    values = self.expressions(expression, key="values", flat=True)
1073                    values = f"{delimiters[0]}{values}{delimiters[1]}"
1074            elif type_value == exp.DataType.Type.INTERVAL:
1075                nested = f" {interior}"
1076            else:
1077                nested = f"({interior})"
1078
1079        type_sql = f"{type_sql}{nested}{values}"
1080        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
1081            exp.DataType.Type.TIMETZ,
1082            exp.DataType.Type.TIMESTAMPTZ,
1083        ):
1084            type_sql = f"{type_sql} WITH TIME ZONE"
1085
1086        return type_sql
def directory_sql(self, expression: sqlglot.expressions.Directory) -> str:
1088    def directory_sql(self, expression: exp.Directory) -> str:
1089        local = "LOCAL " if expression.args.get("local") else ""
1090        row_format = self.sql(expression, "row_format")
1091        row_format = f" {row_format}" if row_format else ""
1092        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
def delete_sql(self, expression: sqlglot.expressions.Delete) -> str:
1094    def delete_sql(self, expression: exp.Delete) -> str:
1095        this = self.sql(expression, "this")
1096        this = f" FROM {this}" if this else ""
1097        using = self.sql(expression, "using")
1098        using = f" USING {using}" if using else ""
1099        where = self.sql(expression, "where")
1100        returning = self.sql(expression, "returning")
1101        limit = self.sql(expression, "limit")
1102        tables = self.expressions(expression, key="tables")
1103        tables = f" {tables}" if tables else ""
1104        if self.RETURNING_END:
1105            expression_sql = f"{this}{using}{where}{returning}{limit}"
1106        else:
1107            expression_sql = f"{returning}{this}{using}{where}{limit}"
1108        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
def drop_sql(self, expression: sqlglot.expressions.Drop) -> str:
1110    def drop_sql(self, expression: exp.Drop) -> str:
1111        this = self.sql(expression, "this")
1112        kind = expression.args["kind"]
1113        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
1114        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
1115        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
1116        cascade = " CASCADE" if expression.args.get("cascade") else ""
1117        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
1118        purge = " PURGE" if expression.args.get("purge") else ""
1119        return (
1120            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
1121        )
def except_sql(self, expression: sqlglot.expressions.Except) -> str:
1123    def except_sql(self, expression: exp.Except) -> str:
1124        return self.prepend_ctes(
1125            expression,
1126            self.set_operation(expression, self.except_op(expression)),
1127        )
def except_op(self, expression: sqlglot.expressions.Except) -> str:
1129    def except_op(self, expression: exp.Except) -> str:
1130        return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}"
def fetch_sql(self, expression: sqlglot.expressions.Fetch) -> str:
1132    def fetch_sql(self, expression: exp.Fetch) -> str:
1133        direction = expression.args.get("direction")
1134        direction = f" {direction}" if direction else ""
1135        count = expression.args.get("count")
1136        count = f" {count}" if count else ""
1137        if expression.args.get("percent"):
1138            count = f"{count} PERCENT"
1139        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
1140        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
def filter_sql(self, expression: sqlglot.expressions.Filter) -> str:
1142    def filter_sql(self, expression: exp.Filter) -> str:
1143        if self.AGGREGATE_FILTER_SUPPORTED:
1144            this = self.sql(expression, "this")
1145            where = self.sql(expression, "expression").strip()
1146            return f"{this} FILTER({where})"
1147
1148        agg = expression.this
1149        agg_arg = agg.this
1150        cond = expression.expression.this
1151        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
1152        return self.sql(agg)
def hint_sql(self, expression: sqlglot.expressions.Hint) -> str:
1154    def hint_sql(self, expression: exp.Hint) -> str:
1155        if not self.QUERY_HINTS:
1156            self.unsupported("Hints are not supported")
1157            return ""
1158
1159        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"
def index_sql(self, expression: sqlglot.expressions.Index) -> str:
1161    def index_sql(self, expression: exp.Index) -> str:
1162        unique = "UNIQUE " if expression.args.get("unique") else ""
1163        primary = "PRIMARY " if expression.args.get("primary") else ""
1164        amp = "AMP " if expression.args.get("amp") else ""
1165        name = self.sql(expression, "this")
1166        name = f"{name} " if name else ""
1167        table = self.sql(expression, "table")
1168        table = f"{self.INDEX_ON} {table}" if table else ""
1169        using = self.sql(expression, "using")
1170        using = f" USING {using}" if using else ""
1171        index = "INDEX " if not table else ""
1172        columns = self.expressions(expression, key="columns", flat=True)
1173        columns = f"({columns})" if columns else ""
1174        partition_by = self.expressions(expression, key="partition_by", flat=True)
1175        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
1176        where = self.sql(expression, "where")
1177        include = self.expressions(expression, key="include", flat=True)
1178        if include:
1179            include = f" INCLUDE ({include})"
1180        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"
def identifier_sql(self, expression: sqlglot.expressions.Identifier) -> str:
1182    def identifier_sql(self, expression: exp.Identifier) -> str:
1183        text = expression.name
1184        lower = text.lower()
1185        text = lower if self.normalize and not expression.quoted else text
1186        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
1187        if (
1188            expression.quoted
1189            or self.dialect.can_identify(text, self.identify)
1190            or lower in self.RESERVED_KEYWORDS
1191            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
1192        ):
1193            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
1194        return text
def inputoutputformat_sql(self, expression: sqlglot.expressions.InputOutputFormat) -> str:
1196    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
1197        input_format = self.sql(expression, "input_format")
1198        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
1199        output_format = self.sql(expression, "output_format")
1200        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
1201        return self.sep().join((input_format, output_format))
def national_sql(self, expression: sqlglot.expressions.National, prefix: str = 'N') -> str:
1203    def national_sql(self, expression: exp.National, prefix: str = "N") -> str:
1204        string = self.sql(exp.Literal.string(expression.name))
1205        return f"{prefix}{string}"
def partition_sql(self, expression: sqlglot.expressions.Partition) -> str:
1207    def partition_sql(self, expression: exp.Partition) -> str:
1208        return f"PARTITION({self.expressions(expression, flat=True)})"
def properties_sql(self, expression: sqlglot.expressions.Properties) -> str:
1210    def properties_sql(self, expression: exp.Properties) -> str:
1211        root_properties = []
1212        with_properties = []
1213
1214        for p in expression.expressions:
1215            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1216            if p_loc == exp.Properties.Location.POST_WITH:
1217                with_properties.append(p)
1218            elif p_loc == exp.Properties.Location.POST_SCHEMA:
1219                root_properties.append(p)
1220
1221        return self.root_properties(
1222            exp.Properties(expressions=root_properties)
1223        ) + self.with_properties(exp.Properties(expressions=with_properties))
def root_properties(self, properties: sqlglot.expressions.Properties) -> str:
1225    def root_properties(self, properties: exp.Properties) -> str:
1226        if properties.expressions:
1227            return self.sep() + self.expressions(properties, indent=False, sep=" ")
1228        return ""
def properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1230    def properties(
1231        self,
1232        properties: exp.Properties,
1233        prefix: str = "",
1234        sep: str = ", ",
1235        suffix: str = "",
1236        wrapped: bool = True,
1237    ) -> str:
1238        if properties.expressions:
1239            expressions = self.expressions(properties, sep=sep, indent=False)
1240            if expressions:
1241                expressions = self.wrap(expressions) if wrapped else expressions
1242                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
1243        return ""
def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
1245    def with_properties(self, properties: exp.Properties) -> str:
1246        return self.properties(properties, prefix=self.seg("WITH"))
def locate_properties(self, properties: sqlglot.expressions.Properties) -> DefaultDict:
1248    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
1249        properties_locs = defaultdict(list)
1250        for p in properties.expressions:
1251            p_loc = self.PROPERTIES_LOCATION[p.__class__]
1252            if p_loc != exp.Properties.Location.UNSUPPORTED:
1253                properties_locs[p_loc].append(p)
1254            else:
1255                self.unsupported(f"Unsupported property {p.key}")
1256
1257        return properties_locs
def property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1259    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
1260        if isinstance(expression.this, exp.Dot):
1261            return self.sql(expression, "this")
1262        return f"'{expression.name}'" if string_key else expression.name
def property_sql(self, expression: sqlglot.expressions.Property) -> str:
1264    def property_sql(self, expression: exp.Property) -> str:
1265        property_cls = expression.__class__
1266        if property_cls == exp.Property:
1267            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
1268
1269        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
1270        if not property_name:
1271            self.unsupported(f"Unsupported property {expression.key}")
1272
1273        return f"{property_name}={self.sql(expression, 'this')}"
def likeproperty_sql(self, expression: sqlglot.expressions.LikeProperty) -> str:
1275    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
1276        if self.SUPPORTS_CREATE_TABLE_LIKE:
1277            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
1278            options = f" {options}" if options else ""
1279
1280            like = f"LIKE {self.sql(expression, 'this')}{options}"
1281            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
1282                like = f"({like})"
1283
1284            return like
1285
1286        if expression.expressions:
1287            self.unsupported("Transpilation of LIKE property options is unsupported")
1288
1289        select = exp.select("*").from_(expression.this).limit(0)
1290        return f"AS {self.sql(select)}"
def fallbackproperty_sql(self, expression: sqlglot.expressions.FallbackProperty) -> str:
1292    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
1293        no = "NO " if expression.args.get("no") else ""
1294        protection = " PROTECTION" if expression.args.get("protection") else ""
1295        return f"{no}FALLBACK{protection}"
def journalproperty_sql(self, expression: sqlglot.expressions.JournalProperty) -> str:
1297    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
1298        no = "NO " if expression.args.get("no") else ""
1299        local = expression.args.get("local")
1300        local = f"{local} " if local else ""
1301        dual = "DUAL " if expression.args.get("dual") else ""
1302        before = "BEFORE " if expression.args.get("before") else ""
1303        after = "AFTER " if expression.args.get("after") else ""
1304        return f"{no}{local}{dual}{before}{after}JOURNAL"
def freespaceproperty_sql(self, expression: sqlglot.expressions.FreespaceProperty) -> str:
1306    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
1307        freespace = self.sql(expression, "this")
1308        percent = " PERCENT" if expression.args.get("percent") else ""
1309        return f"FREESPACE={freespace}{percent}"
def checksumproperty_sql(self, expression: sqlglot.expressions.ChecksumProperty) -> str:
1311    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
1312        if expression.args.get("default"):
1313            property = "DEFAULT"
1314        elif expression.args.get("on"):
1315            property = "ON"
1316        else:
1317            property = "OFF"
1318        return f"CHECKSUM={property}"
def mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1320    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
1321        if expression.args.get("no"):
1322            return "NO MERGEBLOCKRATIO"
1323        if expression.args.get("default"):
1324            return "DEFAULT MERGEBLOCKRATIO"
1325
1326        percent = " PERCENT" if expression.args.get("percent") else ""
1327        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
def datablocksizeproperty_sql(self, expression: sqlglot.expressions.DataBlocksizeProperty) -> str:
1329    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
1330        default = expression.args.get("default")
1331        minimum = expression.args.get("minimum")
1332        maximum = expression.args.get("maximum")
1333        if default or minimum or maximum:
1334            if default:
1335                prop = "DEFAULT"
1336            elif minimum:
1337                prop = "MINIMUM"
1338            else:
1339                prop = "MAXIMUM"
1340            return f"{prop} DATABLOCKSIZE"
1341        units = expression.args.get("units")
1342        units = f" {units}" if units else ""
1343        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1345    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
1346        autotemp = expression.args.get("autotemp")
1347        always = expression.args.get("always")
1348        default = expression.args.get("default")
1349        manual = expression.args.get("manual")
1350        never = expression.args.get("never")
1351
1352        if autotemp is not None:
1353            prop = f"AUTOTEMP({self.expressions(autotemp)})"
1354        elif always:
1355            prop = "ALWAYS"
1356        elif default:
1357            prop = "DEFAULT"
1358        elif manual:
1359            prop = "MANUAL"
1360        elif never:
1361            prop = "NEVER"
1362        return f"BLOCKCOMPRESSION={prop}"
def isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1364    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
1365        no = expression.args.get("no")
1366        no = " NO" if no else ""
1367        concurrent = expression.args.get("concurrent")
1368        concurrent = " CONCURRENT" if concurrent else ""
1369
1370        for_ = ""
1371        if expression.args.get("for_all"):
1372            for_ = " FOR ALL"
1373        elif expression.args.get("for_insert"):
1374            for_ = " FOR INSERT"
1375        elif expression.args.get("for_none"):
1376            for_ = " FOR NONE"
1377        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
def partitionboundspec_sql(self, expression: sqlglot.expressions.PartitionBoundSpec) -> str:
1379    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
1380        if isinstance(expression.this, list):
1381            return f"IN ({self.expressions(expression, key='this', flat=True)})"
1382        if expression.this:
1383            modulus = self.sql(expression, "this")
1384            remainder = self.sql(expression, "expression")
1385            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
1386
1387        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
1388        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
1389        return f"FROM ({from_expressions}) TO ({to_expressions})"
def partitionedofproperty_sql(self, expression: sqlglot.expressions.PartitionedOfProperty) -> str:
1391    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
1392        this = self.sql(expression, "this")
1393
1394        for_values_or_default = expression.expression
1395        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
1396            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
1397        else:
1398            for_values_or_default = " DEFAULT"
1399
1400        return f"PARTITION OF {this}{for_values_or_default}"
def lockingproperty_sql(self, expression: sqlglot.expressions.LockingProperty) -> str:
1402    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
1403        kind = expression.args.get("kind")
1404        this = f" {self.sql(expression, 'this')}" if expression.this else ""
1405        for_or_in = expression.args.get("for_or_in")
1406        for_or_in = f" {for_or_in}" if for_or_in else ""
1407        lock_type = expression.args.get("lock_type")
1408        override = " OVERRIDE" if expression.args.get("override") else ""
1409        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
def withdataproperty_sql(self, expression: sqlglot.expressions.WithDataProperty) -> str:
1411    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
1412        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
1413        statistics = expression.args.get("statistics")
1414        statistics_sql = ""
1415        if statistics is not None:
1416            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
1417        return f"{data_sql}{statistics_sql}"
def withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1419    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
1420        sql = "WITH(SYSTEM_VERSIONING=ON"
1421
1422        if expression.this:
1423            history_table = self.sql(expression, "this")
1424            sql = f"{sql}(HISTORY_TABLE={history_table}"
1425
1426            if expression.expression:
1427                data_consistency_check = self.sql(expression, "expression")
1428                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
1429
1430            sql = f"{sql})"
1431
1432        return f"{sql})"
def insert_sql(self, expression: sqlglot.expressions.Insert) -> str:
1434    def insert_sql(self, expression: exp.Insert) -> str:
1435        overwrite = expression.args.get("overwrite")
1436
1437        if isinstance(expression.this, exp.Directory):
1438            this = " OVERWRITE" if overwrite else " INTO"
1439        else:
1440            this = self.INSERT_OVERWRITE if overwrite else " INTO"
1441
1442        alternative = expression.args.get("alternative")
1443        alternative = f" OR {alternative}" if alternative else ""
1444        ignore = " IGNORE" if expression.args.get("ignore") else ""
1445
1446        this = f"{this} {self.sql(expression, 'this')}"
1447
1448        exists = " IF EXISTS" if expression.args.get("exists") else ""
1449        partition_sql = (
1450            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
1451        )
1452        where = self.sql(expression, "where")
1453        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
1454        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
1455        conflict = self.sql(expression, "conflict")
1456        by_name = " BY NAME" if expression.args.get("by_name") else ""
1457        returning = self.sql(expression, "returning")
1458
1459        if self.RETURNING_END:
1460            expression_sql = f"{expression_sql}{conflict}{returning}"
1461        else:
1462            expression_sql = f"{returning}{expression_sql}{conflict}"
1463
1464        sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
1465        return self.prepend_ctes(expression, sql)
def intersect_sql(self, expression: sqlglot.expressions.Intersect) -> str:
1467    def intersect_sql(self, expression: exp.Intersect) -> str:
1468        return self.prepend_ctes(
1469            expression,
1470            self.set_operation(expression, self.intersect_op(expression)),
1471        )
def intersect_op(self, expression: sqlglot.expressions.Intersect) -> str:
1473    def intersect_op(self, expression: exp.Intersect) -> str:
1474        return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}"
def introducer_sql(self, expression: sqlglot.expressions.Introducer) -> str:
1476    def introducer_sql(self, expression: exp.Introducer) -> str:
1477        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def kill_sql(self, expression: sqlglot.expressions.Kill) -> str:
1479    def kill_sql(self, expression: exp.Kill) -> str:
1480        kind = self.sql(expression, "kind")
1481        kind = f" {kind}" if kind else ""
1482        this = self.sql(expression, "this")
1483        this = f" {this}" if this else ""
1484        return f"KILL{kind}{this}"
def pseudotype_sql(self, expression: sqlglot.expressions.PseudoType) -> str:
1486    def pseudotype_sql(self, expression: exp.PseudoType) -> str:
1487        return expression.name
def objectidentifier_sql(self, expression: sqlglot.expressions.ObjectIdentifier) -> str:
1489    def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str:
1490        return expression.name
def onconflict_sql(self, expression: sqlglot.expressions.OnConflict) -> str:
1492    def onconflict_sql(self, expression: exp.OnConflict) -> str:
1493        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
1494        constraint = self.sql(expression, "constraint")
1495        if constraint:
1496            constraint = f"ON CONSTRAINT {constraint}"
1497        key = self.expressions(expression, key="key", flat=True)
1498        do = "" if expression.args.get("duplicate") else " DO "
1499        nothing = "NOTHING" if expression.args.get("nothing") else ""
1500        expressions = self.expressions(expression, flat=True)
1501        set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
1502        if expressions:
1503            expressions = f"UPDATE {set_keyword}{expressions}"
1504        return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
def returning_sql(self, expression: sqlglot.expressions.Returning) -> str:
1506    def returning_sql(self, expression: exp.Returning) -> str:
1507        return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}"
def rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1509    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
1510        fields = expression.args.get("fields")
1511        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
1512        escaped = expression.args.get("escaped")
1513        escaped = f" ESCAPED BY {escaped}" if escaped else ""
1514        items = expression.args.get("collection_items")
1515        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
1516        keys = expression.args.get("map_keys")
1517        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
1518        lines = expression.args.get("lines")
1519        lines = f" LINES TERMINATED BY {lines}" if lines else ""
1520        null = expression.args.get("null")
1521        null = f" NULL DEFINED AS {null}" if null else ""
1522        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
def withtablehint_sql(self, expression: sqlglot.expressions.WithTableHint) -> str:
1524    def withtablehint_sql(self, expression: exp.WithTableHint) -> str:
1525        return f"WITH ({self.expressions(expression, flat=True)})"
def indextablehint_sql(self, expression: sqlglot.expressions.IndexTableHint) -> str:
1527    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
1528        this = f"{self.sql(expression, 'this')} INDEX"
1529        target = self.sql(expression, "target")
1530        target = f" FOR {target}" if target else ""
1531        return f"{this}{target} ({self.expressions(expression, flat=True)})"
def historicaldata_sql(self, expression: sqlglot.expressions.HistoricalData) -> str:
1533    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
1534        this = self.sql(expression, "this")
1535        kind = self.sql(expression, "kind")
1536        expr = self.sql(expression, "expression")
1537        return f"{this} ({kind} => {expr})"
def table_sql(self, expression: sqlglot.expressions.Table, sep: str = ' AS ') -> str:
1539    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
1540        table = ".".join(
1541            self.sql(part)
1542            for part in (
1543                expression.args.get("catalog"),
1544                expression.args.get("db"),
1545                expression.args.get("this"),
1546            )
1547            if part is not None
1548        )
1549
1550        version = self.sql(expression, "version")
1551        version = f" {version}" if version else ""
1552        alias = self.sql(expression, "alias")
1553        alias = f"{sep}{alias}" if alias else ""
1554        hints = self.expressions(expression, key="hints", sep=" ")
1555        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
1556        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
1557        pivots = f" {pivots}" if pivots else ""
1558        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
1559        laterals = self.expressions(expression, key="laterals", sep="")
1560
1561        file_format = self.sql(expression, "format")
1562        if file_format:
1563            pattern = self.sql(expression, "pattern")
1564            pattern = f", PATTERN => {pattern}" if pattern else ""
1565            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
1566
1567        ordinality = expression.args.get("ordinality") or ""
1568        if ordinality:
1569            ordinality = f" WITH ORDINALITY{alias}"
1570            alias = ""
1571
1572        when = self.sql(expression, "when")
1573        if when:
1574            table = f"{table} {when}"
1575
1576        return f"{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:
1578    def tablesample_sql(
1579        self,
1580        expression: exp.TableSample,
1581        sep: str = " AS ",
1582        tablesample_keyword: t.Optional[str] = None,
1583    ) -> str:
1584        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
1585            table = expression.this.copy()
1586            table.set("alias", None)
1587            this = self.sql(table)
1588            alias = f"{sep}{self.sql(expression.this, 'alias')}"
1589        else:
1590            this = self.sql(expression, "this")
1591            alias = ""
1592
1593        method = self.sql(expression, "method")
1594        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
1595        numerator = self.sql(expression, "bucket_numerator")
1596        denominator = self.sql(expression, "bucket_denominator")
1597        field = self.sql(expression, "bucket_field")
1598        field = f" ON {field}" if field else ""
1599        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
1600        seed = self.sql(expression, "seed")
1601        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
1602
1603        size = self.sql(expression, "size")
1604        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
1605            size = f"{size} ROWS"
1606
1607        percent = self.sql(expression, "percent")
1608        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
1609            percent = f"{percent} PERCENT"
1610
1611        expr = f"{bucket}{percent}{size}"
1612        if self.TABLESAMPLE_REQUIRES_PARENS:
1613            expr = f"({expr})"
1614
1615        return (
1616            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
1617        )
def pivot_sql(self, expression: sqlglot.expressions.Pivot) -> str:
1619    def pivot_sql(self, expression: exp.Pivot) -> str:
1620        expressions = self.expressions(expression, flat=True)
1621
1622        if expression.this:
1623            this = self.sql(expression, "this")
1624            if not expressions:
1625                return f"UNPIVOT {this}"
1626
1627            on = f"{self.seg('ON')} {expressions}"
1628            using = self.expressions(expression, key="using", flat=True)
1629            using = f"{self.seg('USING')} {using}" if using else ""
1630            group = self.sql(expression, "group")
1631            return f"PIVOT {this}{on}{using}{group}"
1632
1633        alias = self.sql(expression, "alias")
1634        alias = f" AS {alias}" if alias else ""
1635        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
1636        field = self.sql(expression, "field")
1637        include_nulls = expression.args.get("include_nulls")
1638        if include_nulls is not None:
1639            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
1640        else:
1641            nulls = ""
1642        return f"{direction}{nulls}({expressions} FOR {field}){alias}"
def version_sql(self, expression: sqlglot.expressions.Version) -> str:
1644    def version_sql(self, expression: exp.Version) -> str:
1645        this = f"FOR {expression.name}"
1646        kind = expression.text("kind")
1647        expr = self.sql(expression, "expression")
1648        return f"{this} {kind} {expr}"
def tuple_sql(self, expression: sqlglot.expressions.Tuple) -> str:
1650    def tuple_sql(self, expression: exp.Tuple) -> str:
1651        return f"({self.expressions(expression, flat=True)})"
def update_sql(self, expression: sqlglot.expressions.Update) -> str:
1653    def update_sql(self, expression: exp.Update) -> str:
1654        this = self.sql(expression, "this")
1655        set_sql = self.expressions(expression, flat=True)
1656        from_sql = self.sql(expression, "from")
1657        where_sql = self.sql(expression, "where")
1658        returning = self.sql(expression, "returning")
1659        order = self.sql(expression, "order")
1660        limit = self.sql(expression, "limit")
1661        if self.RETURNING_END:
1662            expression_sql = f"{from_sql}{where_sql}{returning}"
1663        else:
1664            expression_sql = f"{returning}{from_sql}{where_sql}"
1665        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
1666        return self.prepend_ctes(expression, sql)
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
1668    def values_sql(self, expression: exp.Values) -> str:
1669        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
1670        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
1671            args = self.expressions(expression)
1672            alias = self.sql(expression, "alias")
1673            values = f"VALUES{self.seg('')}{args}"
1674            values = (
1675                f"({values})"
1676                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
1677                else values
1678            )
1679            return f"{values} AS {alias}" if alias else values
1680
1681        # Converts `VALUES...` expression into a series of select unions.
1682        alias_node = expression.args.get("alias")
1683        column_names = alias_node and alias_node.columns
1684
1685        selects: t.List[exp.Subqueryable] = []
1686
1687        for i, tup in enumerate(expression.expressions):
1688            row = tup.expressions
1689
1690            if i == 0 and column_names:
1691                row = [
1692                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
1693                ]
1694
1695            selects.append(exp.Select(expressions=row))
1696
1697        if self.pretty:
1698            # This may result in poor performance for large-cardinality `VALUES` tables, due to
1699            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
1700            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
1701            subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
1702            return self.subquery_sql(
1703                subqueryable.subquery(alias_node and alias_node.this, copy=False)
1704            )
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        return csv(
2075            *sqls,
2076            *[self.sql(join) for join in expression.args.get("joins") or []],
2077            self.sql(expression, "connect"),
2078            self.sql(expression, "match"),
2079            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
2080            self.sql(expression, "where"),
2081            self.sql(expression, "group"),
2082            self.sql(expression, "having"),
2083            *self.after_having_modifiers(expression),
2084            self.sql(expression, "order"),
2085            *offset_limit_modifiers,
2086            *self.after_limit_modifiers(expression),
2087            sep="",
2088        )
def offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2090    def offset_limit_modifiers(
2091        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
2092    ) -> t.List[str]:
2093        return [
2094            self.sql(expression, "offset") if fetch else self.sql(limit),
2095            self.sql(limit) if fetch else self.sql(expression, "offset"),
2096        ]
def after_having_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2098    def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]:
2099        return [
2100            self.sql(expression, "qualify"),
2101            (
2102                self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True)
2103                if expression.args.get("windows")
2104                else ""
2105            ),
2106            self.sql(expression, "distribute"),
2107            self.sql(expression, "sort"),
2108            self.sql(expression, "cluster"),
2109        ]
def after_limit_modifiers(self, expression: sqlglot.expressions.Expression) -> List[str]:
2111    def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]:
2112        locks = self.expressions(expression, key="locks", sep=" ")
2113        locks = f" {locks}" if locks else ""
2114        return [locks, self.sql(expression, "sample")]
def select_sql(self, expression: sqlglot.expressions.Select) -> str:
2116    def select_sql(self, expression: exp.Select) -> str:
2117        into = expression.args.get("into")
2118        if not self.SUPPORTS_SELECT_INTO and into:
2119            into.pop()
2120
2121        hint = self.sql(expression, "hint")
2122        distinct = self.sql(expression, "distinct")
2123        distinct = f" {distinct}" if distinct else ""
2124        kind = self.sql(expression, "kind")
2125        limit = expression.args.get("limit")
2126        top = (
2127            self.limit_sql(limit, top=True)
2128            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
2129            else ""
2130        )
2131
2132        expressions = self.expressions(expression)
2133
2134        if kind:
2135            if kind in self.SELECT_KINDS:
2136                kind = f" AS {kind}"
2137            else:
2138                if kind == "STRUCT":
2139                    expressions = self.expressions(
2140                        sqls=[
2141                            self.sql(
2142                                exp.Struct(
2143                                    expressions=[
2144                                        exp.column(e.output_name).eq(
2145                                            e.this if isinstance(e, exp.Alias) else e
2146                                        )
2147                                        for e in expression.expressions
2148                                    ]
2149                                )
2150                            )
2151                        ]
2152                    )
2153                kind = ""
2154
2155        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
2156        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
2157        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
2158        expressions = f"{self.sep()}{expressions}" if expressions else expressions
2159        sql = self.query_modifiers(
2160            expression,
2161            f"SELECT{top_distinct}{kind}{expressions}",
2162            self.sql(expression, "into", comment=False),
2163            self.sql(expression, "from", comment=False),
2164        )
2165
2166        sql = self.prepend_ctes(expression, sql)
2167
2168        if not self.SUPPORTS_SELECT_INTO and into:
2169            if into.args.get("temporary"):
2170                table_kind = " TEMPORARY"
2171            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
2172                table_kind = " UNLOGGED"
2173            else:
2174                table_kind = ""
2175            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
2176
2177        return sql
def schema_sql(self, expression: sqlglot.expressions.Schema) -> str:
2179    def schema_sql(self, expression: exp.Schema) -> str:
2180        this = self.sql(expression, "this")
2181        sql = self.schema_columns_sql(expression)
2182        return f"{this} {sql}" if this and sql else this or sql
def schema_columns_sql(self, expression: sqlglot.expressions.Schema) -> str:
2184    def schema_columns_sql(self, expression: exp.Schema) -> str:
2185        if expression.expressions:
2186            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
2187        return ""
def star_sql(self, expression: sqlglot.expressions.Star) -> str:
2189    def star_sql(self, expression: exp.Star) -> str:
2190        except_ = self.expressions(expression, key="except", flat=True)
2191        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
2192        replace = self.expressions(expression, key="replace", flat=True)
2193        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
2194        return f"*{except_}{replace}"
def parameter_sql(self, expression: sqlglot.expressions.Parameter) -> str:
2196    def parameter_sql(self, expression: exp.Parameter) -> str:
2197        this = self.sql(expression, "this")
2198        return f"{self.PARAMETER_TOKEN}{this}"
def sessionparameter_sql(self, expression: sqlglot.expressions.SessionParameter) -> str:
2200    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
2201        this = self.sql(expression, "this")
2202        kind = expression.text("kind")
2203        if kind:
2204            kind = f"{kind}."
2205        return f"@@{kind}{this}"
def placeholder_sql(self, expression: sqlglot.expressions.Placeholder) -> str:
2207    def placeholder_sql(self, expression: exp.Placeholder) -> str:
2208        return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.name else "?"
def subquery_sql(self, expression: sqlglot.expressions.Subquery, sep: str = ' AS ') -> str:
2210    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
2211        alias = self.sql(expression, "alias")
2212        alias = f"{sep}{alias}" if alias else ""
2213
2214        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
2215        pivots = f" {pivots}" if pivots else ""
2216
2217        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
2218        return self.prepend_ctes(expression, sql)
def qualify_sql(self, expression: sqlglot.expressions.Qualify) -> str:
2220    def qualify_sql(self, expression: exp.Qualify) -> str:
2221        this = self.indent(self.sql(expression, "this"))
2222        return f"{self.seg('QUALIFY')}{self.sep()}{this}"
def union_sql(self, expression: sqlglot.expressions.Union) -> str:
2224    def union_sql(self, expression: exp.Union) -> str:
2225        return self.prepend_ctes(
2226            expression,
2227            self.set_operation(expression, self.union_op(expression)),
2228        )
def union_op(self, expression: sqlglot.expressions.Union) -> str:
2230    def union_op(self, expression: exp.Union) -> str:
2231        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
2232        kind = kind if expression.args.get("distinct") else " ALL"
2233        by_name = " BY NAME" if expression.args.get("by_name") else ""
2234        return f"UNION{kind}{by_name}"
def unnest_sql(self, expression: sqlglot.expressions.Unnest) -> str:
2236    def unnest_sql(self, expression: exp.Unnest) -> str:
2237        args = self.expressions(expression, flat=True)
2238
2239        alias = expression.args.get("alias")
2240        offset = expression.args.get("offset")
2241
2242        if self.UNNEST_WITH_ORDINALITY:
2243            if alias and isinstance(offset, exp.Expression):
2244                alias.append("columns", offset)
2245
2246        if alias and self.dialect.UNNEST_COLUMN_ONLY:
2247            columns = alias.columns
2248            alias = self.sql(columns[0]) if columns else ""
2249        else:
2250            alias = self.sql(alias)
2251
2252        alias = f" AS {alias}" if alias else alias
2253        if self.UNNEST_WITH_ORDINALITY:
2254            suffix = f" WITH ORDINALITY{alias}" if offset else alias
2255        else:
2256            if isinstance(offset, exp.Expression):
2257                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
2258            elif offset:
2259                suffix = f"{alias} WITH OFFSET"
2260            else:
2261                suffix = alias
2262
2263        return f"UNNEST({args}){suffix}"
def where_sql(self, expression: sqlglot.expressions.Where) -> str:
2265    def where_sql(self, expression: exp.Where) -> str:
2266        this = self.indent(self.sql(expression, "this"))
2267        return f"{self.seg('WHERE')}{self.sep()}{this}"
def window_sql(self, expression: sqlglot.expressions.Window) -> str:
2269    def window_sql(self, expression: exp.Window) -> str:
2270        this = self.sql(expression, "this")
2271        partition = self.partition_by_sql(expression)
2272        order = expression.args.get("order")
2273        order = self.order_sql(order, flat=True) if order else ""
2274        spec = self.sql(expression, "spec")
2275        alias = self.sql(expression, "alias")
2276        over = self.sql(expression, "over") or "OVER"
2277
2278        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
2279
2280        first = expression.args.get("first")
2281        if first is None:
2282            first = ""
2283        else:
2284            first = "FIRST" if first else "LAST"
2285
2286        if not partition and not order and not spec and alias:
2287            return f"{this} {alias}"
2288
2289        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
2290        return f"{this} ({args})"
def partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2292    def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str:
2293        partition = self.expressions(expression, key="partition_by", flat=True)
2294        return f"PARTITION BY {partition}" if partition else ""
def windowspec_sql(self, expression: sqlglot.expressions.WindowSpec) -> str:
2296    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
2297        kind = self.sql(expression, "kind")
2298        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
2299        end = (
2300            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
2301            or "CURRENT ROW"
2302        )
2303        return f"{kind} BETWEEN {start} AND {end}"
def withingroup_sql(self, expression: sqlglot.expressions.WithinGroup) -> str:
2305    def withingroup_sql(self, expression: exp.WithinGroup) -> str:
2306        this = self.sql(expression, "this")
2307        expression_sql = self.sql(expression, "expression")[1:]  # order has a leading space
2308        return f"{this} WITHIN GROUP ({expression_sql})"
def between_sql(self, expression: sqlglot.expressions.Between) -> str:
2310    def between_sql(self, expression: exp.Between) -> str:
2311        this = self.sql(expression, "this")
2312        low = self.sql(expression, "low")
2313        high = self.sql(expression, "high")
2314        return f"{this} BETWEEN {low} AND {high}"
def bracket_sql(self, expression: sqlglot.expressions.Bracket) -> str:
2316    def bracket_sql(self, expression: exp.Bracket) -> str:
2317        expressions = apply_index_offset(
2318            expression.this,
2319            expression.expressions,
2320            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
2321        )
2322        expressions_sql = ", ".join(self.sql(e) for e in expressions)
2323        return f"{self.sql(expression, 'this')}[{expressions_sql}]"
def all_sql(self, expression: sqlglot.expressions.All) -> str:
2325    def all_sql(self, expression: exp.All) -> str:
2326        return f"ALL {self.wrap(expression)}"
def any_sql(self, expression: sqlglot.expressions.Any) -> str:
2328    def any_sql(self, expression: exp.Any) -> str:
2329        this = self.sql(expression, "this")
2330        if isinstance(expression.this, exp.Subqueryable):
2331            this = self.wrap(this)
2332        return f"ANY {this}"
def exists_sql(self, expression: sqlglot.expressions.Exists) -> str:
2334    def exists_sql(self, expression: exp.Exists) -> str:
2335        return f"EXISTS{self.wrap(expression)}"
def case_sql(self, expression: sqlglot.expressions.Case) -> str:
2337    def case_sql(self, expression: exp.Case) -> str:
2338        this = self.sql(expression, "this")
2339        statements = [f"CASE {this}" if this else "CASE"]
2340
2341        for e in expression.args["ifs"]:
2342            statements.append(f"WHEN {self.sql(e, 'this')}")
2343            statements.append(f"THEN {self.sql(e, 'true')}")
2344
2345        default = self.sql(expression, "default")
2346
2347        if default:
2348            statements.append(f"ELSE {default}")
2349
2350        statements.append("END")
2351
2352        if self.pretty and self.text_width(statements) > self.max_text_width:
2353            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
2354
2355        return " ".join(statements)
def constraint_sql(self, expression: sqlglot.expressions.Constraint) -> str:
2357    def constraint_sql(self, expression: exp.Constraint) -> str:
2358        this = self.sql(expression, "this")
2359        expressions = self.expressions(expression, flat=True)
2360        return f"CONSTRAINT {this} {expressions}"
def nextvaluefor_sql(self, expression: sqlglot.expressions.NextValueFor) -> str:
2362    def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str:
2363        order = expression.args.get("order")
2364        order = f" OVER ({self.order_sql(order, flat=True)})" if order else ""
2365        return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}"
def extract_sql(self, expression: sqlglot.expressions.Extract) -> str:
2367    def extract_sql(self, expression: exp.Extract) -> str:
2368        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
2369        expression_sql = self.sql(expression, "expression")
2370        return f"EXTRACT({this} FROM {expression_sql})"
def trim_sql(self, expression: sqlglot.expressions.Trim) -> str:
2372    def trim_sql(self, expression: exp.Trim) -> str:
2373        trim_type = self.sql(expression, "position")
2374
2375        if trim_type == "LEADING":
2376            return self.func("LTRIM", expression.this)
2377        elif trim_type == "TRAILING":
2378            return self.func("RTRIM", expression.this)
2379        else:
2380            return self.func("TRIM", expression.this, expression.expression)
def convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2382    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
2383        args = expression.expressions
2384        if isinstance(expression, exp.ConcatWs):
2385            args = args[1:]  # Skip the delimiter
2386
2387        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2388            args = [exp.cast(e, "text") for e in args]
2389
2390        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
2391            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
2392
2393        return args
def concat_sql(self, expression: sqlglot.expressions.Concat) -> str:
2395    def concat_sql(self, expression: exp.Concat) -> str:
2396        expressions = self.convert_concat_args(expression)
2397
2398        # Some dialects don't allow a single-argument CONCAT call
2399        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
2400            return self.sql(expressions[0])
2401
2402        return self.func("CONCAT", *expressions)
def concatws_sql(self, expression: sqlglot.expressions.ConcatWs) -> str:
2404    def concatws_sql(self, expression: exp.ConcatWs) -> str:
2405        return self.func(
2406            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
2407        )
def check_sql(self, expression: sqlglot.expressions.Check) -> str:
2409    def check_sql(self, expression: exp.Check) -> str:
2410        this = self.sql(expression, key="this")
2411        return f"CHECK ({this})"
def foreignkey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2413    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
2414        expressions = self.expressions(expression, flat=True)
2415        reference = self.sql(expression, "reference")
2416        reference = f" {reference}" if reference else ""
2417        delete = self.sql(expression, "delete")
2418        delete = f" ON DELETE {delete}" if delete else ""
2419        update = self.sql(expression, "update")
2420        update = f" ON UPDATE {update}" if update else ""
2421        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
def primarykey_sql(self, expression: sqlglot.expressions.ForeignKey) -> str:
2423    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
2424        expressions = self.expressions(expression, flat=True)
2425        options = self.expressions(expression, key="options", flat=True, sep=" ")
2426        options = f" {options}" if options else ""
2427        return f"PRIMARY KEY ({expressions}){options}"
def if_sql(self, expression: sqlglot.expressions.If) -> str:
2429    def if_sql(self, expression: exp.If) -> str:
2430        return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false")))
def matchagainst_sql(self, expression: sqlglot.expressions.MatchAgainst) -> str:
2432    def matchagainst_sql(self, expression: exp.MatchAgainst) -> str:
2433        modifier = expression.args.get("modifier")
2434        modifier = f" {modifier}" if modifier else ""
2435        return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})"
def jsonkeyvalue_sql(self, expression: sqlglot.expressions.JSONKeyValue) -> str:
2437    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
2438        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:
2440    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
2441        path = self.expressions(expression, sep="", flat=True).lstrip(".")
2442        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
def json_path_part(self, expression: int | str | sqlglot.expressions.JSONPathPart) -> str:
2444    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
2445        if isinstance(expression, exp.JSONPathPart):
2446            transform = self.TRANSFORMS.get(expression.__class__)
2447            if not callable(transform):
2448                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
2449                return ""
2450
2451            return transform(self, expression)
2452
2453        if isinstance(expression, int):
2454            return str(expression)
2455
2456        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
2457            escaped = expression.replace("'", "\\'")
2458            escaped = f"\\'{expression}\\'"
2459        else:
2460            escaped = expression.replace('"', '\\"')
2461            escaped = f'"{escaped}"'
2462
2463        return escaped
def formatjson_sql(self, expression: sqlglot.expressions.FormatJson) -> str:
2465    def formatjson_sql(self, expression: exp.FormatJson) -> str:
2466        return f"{self.sql(expression, 'this')} FORMAT JSON"
def jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2468    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
2469        null_handling = expression.args.get("null_handling")
2470        null_handling = f" {null_handling}" if null_handling else ""
2471
2472        unique_keys = expression.args.get("unique_keys")
2473        if unique_keys is not None:
2474            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
2475        else:
2476            unique_keys = ""
2477
2478        return_type = self.sql(expression, "return_type")
2479        return_type = f" RETURNING {return_type}" if return_type else ""
2480        encoding = self.sql(expression, "encoding")
2481        encoding = f" ENCODING {encoding}" if encoding else ""
2482
2483        return self.func(
2484            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
2485            *expression.expressions,
2486            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
2487        )
def jsonobjectagg_sql(self, expression: sqlglot.expressions.JSONObjectAgg) -> str:
2489    def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str:
2490        return self.jsonobject_sql(expression)
def jsonarray_sql(self, expression: sqlglot.expressions.JSONArray) -> str:
2492    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
2493        null_handling = expression.args.get("null_handling")
2494        null_handling = f" {null_handling}" if null_handling else ""
2495        return_type = self.sql(expression, "return_type")
2496        return_type = f" RETURNING {return_type}" if return_type else ""
2497        strict = " STRICT" if expression.args.get("strict") else ""
2498        return self.func(
2499            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
2500        )
def jsonarrayagg_sql(self, expression: sqlglot.expressions.JSONArrayAgg) -> str:
2502    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
2503        this = self.sql(expression, "this")
2504        order = self.sql(expression, "order")
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_ARRAYAGG",
2512            this,
2513            suffix=f"{order}{null_handling}{return_type}{strict})",
2514        )
def jsoncolumndef_sql(self, expression: sqlglot.expressions.JSONColumnDef) -> str:
2516    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
2517        path = self.sql(expression, "path")
2518        path = f" PATH {path}" if path else ""
2519        nested_schema = self.sql(expression, "nested_schema")
2520
2521        if nested_schema:
2522            return f"NESTED{path} {nested_schema}"
2523
2524        this = self.sql(expression, "this")
2525        kind = self.sql(expression, "kind")
2526        kind = f" {kind}" if kind else ""
2527        return f"{this}{kind}{path}"
def jsonschema_sql(self, expression: sqlglot.expressions.JSONSchema) -> str:
2529    def jsonschema_sql(self, expression: exp.JSONSchema) -> str:
2530        return self.func("COLUMNS", *expression.expressions)
def jsontable_sql(self, expression: sqlglot.expressions.JSONTable) -> str:
2532    def jsontable_sql(self, expression: exp.JSONTable) -> str:
2533        this = self.sql(expression, "this")
2534        path = self.sql(expression, "path")
2535        path = f", {path}" if path else ""
2536        error_handling = expression.args.get("error_handling")
2537        error_handling = f" {error_handling}" if error_handling else ""
2538        empty_handling = expression.args.get("empty_handling")
2539        empty_handling = f" {empty_handling}" if empty_handling else ""
2540        schema = self.sql(expression, "schema")
2541        return self.func(
2542            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
2543        )
def openjsoncolumndef_sql(self, expression: sqlglot.expressions.OpenJSONColumnDef) -> str:
2545    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
2546        this = self.sql(expression, "this")
2547        kind = self.sql(expression, "kind")
2548        path = self.sql(expression, "path")
2549        path = f" {path}" if path else ""
2550        as_json = " AS JSON" if expression.args.get("as_json") else ""
2551        return f"{this} {kind}{path}{as_json}"
def openjson_sql(self, expression: sqlglot.expressions.OpenJSON) -> str:
2553    def openjson_sql(self, expression: exp.OpenJSON) -> str:
2554        this = self.sql(expression, "this")
2555        path = self.sql(expression, "path")
2556        path = f", {path}" if path else ""
2557        expressions = self.expressions(expression)
2558        with_ = (
2559            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
2560            if expressions
2561            else ""
2562        )
2563        return f"OPENJSON({this}{path}){with_}"
def in_sql(self, expression: sqlglot.expressions.In) -> str:
2565    def in_sql(self, expression: exp.In) -> str:
2566        query = expression.args.get("query")
2567        unnest = expression.args.get("unnest")
2568        field = expression.args.get("field")
2569        is_global = " GLOBAL" if expression.args.get("is_global") else ""
2570
2571        if query:
2572            in_sql = self.wrap(query)
2573        elif unnest:
2574            in_sql = self.in_unnest_op(unnest)
2575        elif field:
2576            in_sql = self.sql(field)
2577        else:
2578            in_sql = f"({self.expressions(expression, flat=True)})"
2579
2580        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
def in_unnest_op(self, unnest: sqlglot.expressions.Unnest) -> str:
2582    def in_unnest_op(self, unnest: exp.Unnest) -> str:
2583        return f"(SELECT {self.sql(unnest)})"
def interval_sql(self, expression: sqlglot.expressions.Interval) -> str:
2585    def interval_sql(self, expression: exp.Interval) -> str:
2586        unit = self.sql(expression, "unit")
2587        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
2588            unit = self.TIME_PART_SINGULARS.get(unit, unit)
2589        unit = f" {unit}" if unit else ""
2590
2591        if self.SINGLE_STRING_INTERVAL:
2592            this = expression.this.name if expression.this else ""
2593            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
2594
2595        this = self.sql(expression, "this")
2596        if this:
2597            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
2598            this = f" {this}" if unwrapped else f" ({this})"
2599
2600        return f"INTERVAL{this}{unit}"
def return_sql(self, expression: sqlglot.expressions.Return) -> str:
2602    def return_sql(self, expression: exp.Return) -> str:
2603        return f"RETURN {self.sql(expression, 'this')}"
def reference_sql(self, expression: sqlglot.expressions.Reference) -> str:
2605    def reference_sql(self, expression: exp.Reference) -> str:
2606        this = self.sql(expression, "this")
2607        expressions = self.expressions(expression, flat=True)
2608        expressions = f"({expressions})" if expressions else ""
2609        options = self.expressions(expression, key="options", flat=True, sep=" ")
2610        options = f" {options}" if options else ""
2611        return f"REFERENCES {this}{expressions}{options}"
def anonymous_sql(self, expression: sqlglot.expressions.Anonymous) -> str:
2613    def anonymous_sql(self, expression: exp.Anonymous) -> str:
2614        return self.func(expression.name, *expression.expressions)
def paren_sql(self, expression: sqlglot.expressions.Paren) -> str:
2616    def paren_sql(self, expression: exp.Paren) -> str:
2617        if isinstance(expression.unnest(), exp.Select):
2618            sql = self.wrap(expression)
2619        else:
2620            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
2621            sql = f"({sql}{self.seg(')', sep='')}"
2622
2623        return self.prepend_ctes(expression, sql)
def neg_sql(self, expression: sqlglot.expressions.Neg) -> str:
2625    def neg_sql(self, expression: exp.Neg) -> str:
2626        # This makes sure we don't convert "- - 5" to "--5", which is a comment
2627        this_sql = self.sql(expression, "this")
2628        sep = " " if this_sql[0] == "-" else ""
2629        return f"-{sep}{this_sql}"
def not_sql(self, expression: sqlglot.expressions.Not) -> str:
2631    def not_sql(self, expression: exp.Not) -> str:
2632        return f"NOT {self.sql(expression, 'this')}"
def alias_sql(self, expression: sqlglot.expressions.Alias) -> str:
2634    def alias_sql(self, expression: exp.Alias) -> str:
2635        alias = self.sql(expression, "alias")
2636        alias = f" AS {alias}" if alias else ""
2637        return f"{self.sql(expression, 'this')}{alias}"
def pivotalias_sql(self, expression: sqlglot.expressions.PivotAlias) -> str:
2639    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
2640        alias = expression.args["alias"]
2641        identifier_alias = isinstance(alias, exp.Identifier)
2642
2643        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2644            alias.replace(exp.Literal.string(alias.output_name))
2645        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
2646            alias.replace(exp.to_identifier(alias.output_name))
2647
2648        return self.alias_sql(expression)
def aliases_sql(self, expression: sqlglot.expressions.Aliases) -> str:
2650    def aliases_sql(self, expression: exp.Aliases) -> str:
2651        return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
def atindex_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2653    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
2654        this = self.sql(expression, "this")
2655        index = self.sql(expression, "expression")
2656        return f"{this} AT {index}"
def attimezone_sql(self, expression: sqlglot.expressions.AtTimeZone) -> str:
2658    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
2659        this = self.sql(expression, "this")
2660        zone = self.sql(expression, "zone")
2661        return f"{this} AT TIME ZONE {zone}"
def fromtimezone_sql(self, expression: sqlglot.expressions.FromTimeZone) -> str:
2663    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
2664        this = self.sql(expression, "this")
2665        zone = self.sql(expression, "zone")
2666        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"
def add_sql(self, expression: sqlglot.expressions.Add) -> str:
2668    def add_sql(self, expression: exp.Add) -> str:
2669        return self.binary(expression, "+")
def and_sql(self, expression: sqlglot.expressions.And) -> str:
2671    def and_sql(self, expression: exp.And) -> str:
2672        return self.connector_sql(expression, "AND")
def xor_sql(self, expression: sqlglot.expressions.Xor) -> str:
2674    def xor_sql(self, expression: exp.Xor) -> str:
2675        return self.connector_sql(expression, "XOR")
def connector_sql(self, expression: sqlglot.expressions.Connector, op: str) -> str:
2677    def connector_sql(self, expression: exp.Connector, op: str) -> str:
2678        if not self.pretty:
2679            return self.binary(expression, op)
2680
2681        sqls = tuple(
2682            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
2683            for i, e in enumerate(expression.flatten(unnest=False))
2684        )
2685
2686        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
2687        return f"{sep}{op} ".join(sqls)
def bitwiseand_sql(self, expression: sqlglot.expressions.BitwiseAnd) -> str:
2689    def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str:
2690        return self.binary(expression, "&")
def bitwiseleftshift_sql(self, expression: sqlglot.expressions.BitwiseLeftShift) -> str:
2692    def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str:
2693        return self.binary(expression, "<<")
def bitwisenot_sql(self, expression: sqlglot.expressions.BitwiseNot) -> str:
2695    def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str:
2696        return f"~{self.sql(expression, 'this')}"
def bitwiseor_sql(self, expression: sqlglot.expressions.BitwiseOr) -> str:
2698    def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str:
2699        return self.binary(expression, "|")
def bitwiserightshift_sql(self, expression: sqlglot.expressions.BitwiseRightShift) -> str:
2701    def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str:
2702        return self.binary(expression, ">>")
def bitwisexor_sql(self, expression: sqlglot.expressions.BitwiseXor) -> str:
2704    def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str:
2705        return self.binary(expression, "^")
def cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2707    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
2708        format_sql = self.sql(expression, "format")
2709        format_sql = f" FORMAT {format_sql}" if format_sql else ""
2710        to_sql = self.sql(expression, "to")
2711        to_sql = f" {to_sql}" if to_sql else ""
2712        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
def currentdate_sql(self, expression: sqlglot.expressions.CurrentDate) -> str:
2714    def currentdate_sql(self, expression: exp.CurrentDate) -> str:
2715        zone = self.sql(expression, "this")
2716        return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE"
def currenttimestamp_sql(self, expression: sqlglot.expressions.CurrentTimestamp) -> str:
2718    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
2719        return self.func("CURRENT_TIMESTAMP", expression.this)
def collate_sql(self, expression: sqlglot.expressions.Collate) -> str:
2721    def collate_sql(self, expression: exp.Collate) -> str:
2722        if self.COLLATE_IS_FUNC:
2723            return self.function_fallback_sql(expression)
2724        return self.binary(expression, "COLLATE")
def command_sql(self, expression: sqlglot.expressions.Command) -> str:
2726    def command_sql(self, expression: exp.Command) -> str:
2727        return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}"
def comment_sql(self, expression: sqlglot.expressions.Comment) -> str:
2729    def comment_sql(self, expression: exp.Comment) -> str:
2730        this = self.sql(expression, "this")
2731        kind = expression.args["kind"]
2732        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
2733        expression_sql = self.sql(expression, "expression")
2734        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
def mergetreettlaction_sql(self, expression: sqlglot.expressions.MergeTreeTTLAction) -> str:
2736    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
2737        this = self.sql(expression, "this")
2738        delete = " DELETE" if expression.args.get("delete") else ""
2739        recompress = self.sql(expression, "recompress")
2740        recompress = f" RECOMPRESS {recompress}" if recompress else ""
2741        to_disk = self.sql(expression, "to_disk")
2742        to_disk = f" TO DISK {to_disk}" if to_disk else ""
2743        to_volume = self.sql(expression, "to_volume")
2744        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
2745        return f"{this}{delete}{recompress}{to_disk}{to_volume}"
def mergetreettl_sql(self, expression: sqlglot.expressions.MergeTreeTTL) -> str:
2747    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
2748        where = self.sql(expression, "where")
2749        group = self.sql(expression, "group")
2750        aggregates = self.expressions(expression, key="aggregates")
2751        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
2752
2753        if not (where or group or aggregates) and len(expression.expressions) == 1:
2754            return f"TTL {self.expressions(expression, flat=True)}"
2755
2756        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
def transaction_sql(self, expression: sqlglot.expressions.Transaction) -> str:
2758    def transaction_sql(self, expression: exp.Transaction) -> str:
2759        return "BEGIN"
def commit_sql(self, expression: sqlglot.expressions.Commit) -> str:
2761    def commit_sql(self, expression: exp.Commit) -> str:
2762        chain = expression.args.get("chain")
2763        if chain is not None:
2764            chain = " AND CHAIN" if chain else " AND NO CHAIN"
2765
2766        return f"COMMIT{chain or ''}"
def rollback_sql(self, expression: sqlglot.expressions.Rollback) -> str:
2768    def rollback_sql(self, expression: exp.Rollback) -> str:
2769        savepoint = expression.args.get("savepoint")
2770        savepoint = f" TO {savepoint}" if savepoint else ""
2771        return f"ROLLBACK{savepoint}"
def altercolumn_sql(self, expression: sqlglot.expressions.AlterColumn) -> str:
2773    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
2774        this = self.sql(expression, "this")
2775
2776        dtype = self.sql(expression, "dtype")
2777        if dtype:
2778            collate = self.sql(expression, "collate")
2779            collate = f" COLLATE {collate}" if collate else ""
2780            using = self.sql(expression, "using")
2781            using = f" USING {using}" if using else ""
2782            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
2783
2784        default = self.sql(expression, "default")
2785        if default:
2786            return f"ALTER COLUMN {this} SET DEFAULT {default}"
2787
2788        comment = self.sql(expression, "comment")
2789        if comment:
2790            return f"ALTER COLUMN {this} COMMENT {comment}"
2791
2792        if not expression.args.get("drop"):
2793            self.unsupported("Unsupported ALTER COLUMN syntax")
2794
2795        return f"ALTER COLUMN {this} DROP DEFAULT"
def renametable_sql(self, expression: sqlglot.expressions.RenameTable) -> str:
2797    def renametable_sql(self, expression: exp.RenameTable) -> str:
2798        if not self.RENAME_TABLE_WITH_DB:
2799            # Remove db from tables
2800            expression = expression.transform(
2801                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
2802            )
2803        this = self.sql(expression, "this")
2804        return f"RENAME TO {this}"
def renamecolumn_sql(self, expression: sqlglot.expressions.RenameColumn) -> str:
2806    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
2807        exists = " IF EXISTS" if expression.args.get("exists") else ""
2808        old_column = self.sql(expression, "this")
2809        new_column = self.sql(expression, "to")
2810        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"
def altertable_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2812    def altertable_sql(self, expression: exp.AlterTable) -> str:
2813        actions = expression.args["actions"]
2814
2815        if isinstance(actions[0], exp.ColumnDef):
2816            actions = self.add_column_sql(expression)
2817        elif isinstance(actions[0], exp.Schema):
2818            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
2819        elif isinstance(actions[0], exp.Delete):
2820            actions = self.expressions(expression, key="actions", flat=True)
2821        else:
2822            actions = self.expressions(expression, key="actions", flat=True)
2823
2824        exists = " IF EXISTS" if expression.args.get("exists") else ""
2825        only = " ONLY" if expression.args.get("only") else ""
2826        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
def add_column_sql(self, expression: sqlglot.expressions.AlterTable) -> str:
2828    def add_column_sql(self, expression: exp.AlterTable) -> str:
2829        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
2830            return self.expressions(
2831                expression,
2832                key="actions",
2833                prefix="ADD COLUMN ",
2834            )
2835        return f"ADD {self.expressions(expression, key='actions', flat=True)}"
def droppartition_sql(self, expression: sqlglot.expressions.DropPartition) -> str:
2837    def droppartition_sql(self, expression: exp.DropPartition) -> str:
2838        expressions = self.expressions(expression)
2839        exists = " IF EXISTS " if expression.args.get("exists") else " "
2840        return f"DROP{exists}{expressions}"
def addconstraint_sql(self, expression: sqlglot.expressions.AddConstraint) -> str:
2842    def addconstraint_sql(self, expression: exp.AddConstraint) -> str:
2843        return f"ADD {self.expressions(expression)}"
def distinct_sql(self, expression: sqlglot.expressions.Distinct) -> str:
2845    def distinct_sql(self, expression: exp.Distinct) -> str:
2846        this = self.expressions(expression, flat=True)
2847
2848        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
2849            case = exp.case()
2850            for arg in expression.expressions:
2851                case = case.when(arg.is_(exp.null()), exp.null())
2852            this = self.sql(case.else_(f"({this})"))
2853
2854        this = f" {this}" if this else ""
2855
2856        on = self.sql(expression, "on")
2857        on = f" ON {on}" if on else ""
2858        return f"DISTINCT{this}{on}"
def ignorenulls_sql(self, expression: sqlglot.expressions.IgnoreNulls) -> str:
2860    def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str:
2861        return self._embed_ignore_nulls(expression, "IGNORE NULLS")
def respectnulls_sql(self, expression: sqlglot.expressions.RespectNulls) -> str:
2863    def respectnulls_sql(self, expression: exp.RespectNulls) -> str:
2864        return self._embed_ignore_nulls(expression, "RESPECT NULLS")
def havingmax_sql(self, expression: sqlglot.expressions.HavingMax) -> str:
2866    def havingmax_sql(self, expression: exp.HavingMax) -> str:
2867        this_sql = self.sql(expression, "this")
2868        expression_sql = self.sql(expression, "expression")
2869        kind = "MAX" if expression.args.get("max") else "MIN"
2870        return f"{this_sql} HAVING {kind} {expression_sql}"
def intdiv_sql(self, expression: sqlglot.expressions.IntDiv) -> str:
2896    def intdiv_sql(self, expression: exp.IntDiv) -> str:
2897        return self.sql(
2898            exp.Cast(
2899                this=exp.Div(this=expression.this, expression=expression.expression),
2900                to=exp.DataType(this=exp.DataType.Type.INT),
2901            )
2902        )
def dpipe_sql(self, expression: sqlglot.expressions.DPipe) -> str:
2904    def dpipe_sql(self, expression: exp.DPipe) -> str:
2905        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
2906            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
2907        return self.binary(expression, "||")
def div_sql(self, expression: sqlglot.expressions.Div) -> str:
2909    def div_sql(self, expression: exp.Div) -> str:
2910        l, r = expression.left, expression.right
2911
2912        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
2913            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
2914
2915        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
2916            if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type(
2917                *exp.DataType.FLOAT_TYPES
2918            ):
2919                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
2920
2921        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
2922            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
2923                return self.sql(
2924                    exp.cast(
2925                        l / r,
2926                        to=exp.DataType.Type.BIGINT,
2927                    )
2928                )
2929
2930        return self.binary(expression, "/")
def overlaps_sql(self, expression: sqlglot.expressions.Overlaps) -> str:
2932    def overlaps_sql(self, expression: exp.Overlaps) -> str:
2933        return self.binary(expression, "OVERLAPS")
def distance_sql(self, expression: sqlglot.expressions.Distance) -> str:
2935    def distance_sql(self, expression: exp.Distance) -> str:
2936        return self.binary(expression, "<->")
def dot_sql(self, expression: sqlglot.expressions.Dot) -> str:
2938    def dot_sql(self, expression: exp.Dot) -> str:
2939        return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}"
def eq_sql(self, expression: sqlglot.expressions.EQ) -> str:
2941    def eq_sql(self, expression: exp.EQ) -> str:
2942        return self.binary(expression, "=")
def propertyeq_sql(self, expression: sqlglot.expressions.PropertyEQ) -> str:
2944    def propertyeq_sql(self, expression: exp.PropertyEQ) -> str:
2945        return self.binary(expression, ":=")
def escape_sql(self, expression: sqlglot.expressions.Escape) -> str:
2947    def escape_sql(self, expression: exp.Escape) -> str:
2948        return self.binary(expression, "ESCAPE")
def glob_sql(self, expression: sqlglot.expressions.Glob) -> str:
2950    def glob_sql(self, expression: exp.Glob) -> str:
2951        return self.binary(expression, "GLOB")
def gt_sql(self, expression: sqlglot.expressions.GT) -> str:
2953    def gt_sql(self, expression: exp.GT) -> str:
2954        return self.binary(expression, ">")
def gte_sql(self, expression: sqlglot.expressions.GTE) -> str:
2956    def gte_sql(self, expression: exp.GTE) -> str:
2957        return self.binary(expression, ">=")
def ilike_sql(self, expression: sqlglot.expressions.ILike) -> str:
2959    def ilike_sql(self, expression: exp.ILike) -> str:
2960        return self.binary(expression, "ILIKE")
def ilikeany_sql(self, expression: sqlglot.expressions.ILikeAny) -> str:
2962    def ilikeany_sql(self, expression: exp.ILikeAny) -> str:
2963        return self.binary(expression, "ILIKE ANY")
def is_sql(self, expression: sqlglot.expressions.Is) -> str:
2965    def is_sql(self, expression: exp.Is) -> str:
2966        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
2967            return self.sql(
2968                expression.this if expression.expression.this else exp.not_(expression.this)
2969            )
2970        return self.binary(expression, "IS")
def like_sql(self, expression: sqlglot.expressions.Like) -> str:
2972    def like_sql(self, expression: exp.Like) -> str:
2973        return self.binary(expression, "LIKE")
def likeany_sql(self, expression: sqlglot.expressions.LikeAny) -> str:
2975    def likeany_sql(self, expression: exp.LikeAny) -> str:
2976        return self.binary(expression, "LIKE ANY")
def similarto_sql(self, expression: sqlglot.expressions.SimilarTo) -> str:
2978    def similarto_sql(self, expression: exp.SimilarTo) -> str:
2979        return self.binary(expression, "SIMILAR TO")
def lt_sql(self, expression: sqlglot.expressions.LT) -> str:
2981    def lt_sql(self, expression: exp.LT) -> str:
2982        return self.binary(expression, "<")
def lte_sql(self, expression: sqlglot.expressions.LTE) -> str:
2984    def lte_sql(self, expression: exp.LTE) -> str:
2985        return self.binary(expression, "<=")
def mod_sql(self, expression: sqlglot.expressions.Mod) -> str:
2987    def mod_sql(self, expression: exp.Mod) -> str:
2988        return self.binary(expression, "%")
def mul_sql(self, expression: sqlglot.expressions.Mul) -> str:
2990    def mul_sql(self, expression: exp.Mul) -> str:
2991        return self.binary(expression, "*")
def neq_sql(self, expression: sqlglot.expressions.NEQ) -> str:
2993    def neq_sql(self, expression: exp.NEQ) -> str:
2994        return self.binary(expression, "<>")
def nullsafeeq_sql(self, expression: sqlglot.expressions.NullSafeEQ) -> str:
2996    def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str:
2997        return self.binary(expression, "IS NOT DISTINCT FROM")
def nullsafeneq_sql(self, expression: sqlglot.expressions.NullSafeNEQ) -> str:
2999    def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str:
3000        return self.binary(expression, "IS DISTINCT FROM")
def or_sql(self, expression: sqlglot.expressions.Or) -> str:
3002    def or_sql(self, expression: exp.Or) -> str:
3003        return self.connector_sql(expression, "OR")
def slice_sql(self, expression: sqlglot.expressions.Slice) -> str:
3005    def slice_sql(self, expression: exp.Slice) -> str:
3006        return self.binary(expression, ":")
def sub_sql(self, expression: sqlglot.expressions.Sub) -> str:
3008    def sub_sql(self, expression: exp.Sub) -> str:
3009        return self.binary(expression, "-")
def trycast_sql(self, expression: sqlglot.expressions.TryCast) -> str:
3011    def trycast_sql(self, expression: exp.TryCast) -> str:
3012        return self.cast_sql(expression, safe_prefix="TRY_")
def log_sql(self, expression: sqlglot.expressions.Log) -> str:
3014    def log_sql(self, expression: exp.Log) -> str:
3015        this = expression.this
3016        expr = expression.expression
3017
3018        if not self.dialect.LOG_BASE_FIRST:
3019            this, expr = expr, this
3020
3021        return self.func("LOG", this, expr)
def use_sql(self, expression: sqlglot.expressions.Use) -> str:
3023    def use_sql(self, expression: exp.Use) -> str:
3024        kind = self.sql(expression, "kind")
3025        kind = f" {kind}" if kind else ""
3026        this = self.sql(expression, "this")
3027        this = f" {this}" if this else ""
3028        return f"USE{kind}{this}"
def binary(self, expression: sqlglot.expressions.Binary, op: str) -> str:
3030    def binary(self, expression: exp.Binary, op: str) -> str:
3031        op = self.maybe_comment(op, comments=expression.comments)
3032        return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}"
def function_fallback_sql(self, expression: sqlglot.expressions.Func) -> str:
3034    def function_fallback_sql(self, expression: exp.Func) -> str:
3035        args = []
3036
3037        for key in expression.arg_types:
3038            arg_value = expression.args.get(key)
3039
3040            if isinstance(arg_value, list):
3041                for value in arg_value:
3042                    args.append(value)
3043            elif arg_value is not None:
3044                args.append(arg_value)
3045
3046        if self.normalize_functions:
3047            name = expression.sql_name()
3048        else:
3049            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
3050
3051        return self.func(name, *args)
def func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3053    def func(
3054        self,
3055        name: str,
3056        *args: t.Optional[exp.Expression | str],
3057        prefix: str = "(",
3058        suffix: str = ")",
3059    ) -> str:
3060        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"
def format_args(self, *args: Union[str, sqlglot.expressions.Expression, NoneType]) -> str:
3062    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
3063        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
3064        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
3065            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
3066        return ", ".join(arg_sqls)
def text_width(self, args: Iterable) -> int:
3068    def text_width(self, args: t.Iterable) -> int:
3069        return sum(len(arg) for arg in args)
def format_time(self, expression: sqlglot.expressions.Expression) -> Optional[str]:
3071    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
3072        return format_time(
3073            self.sql(expression, "format"),
3074            self.dialect.INVERSE_TIME_MAPPING,
3075            self.dialect.INVERSE_TIME_TRIE,
3076        )
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:
3078    def expressions(
3079        self,
3080        expression: t.Optional[exp.Expression] = None,
3081        key: t.Optional[str] = None,
3082        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
3083        flat: bool = False,
3084        indent: bool = True,
3085        skip_first: bool = False,
3086        sep: str = ", ",
3087        prefix: str = "",
3088    ) -> str:
3089        expressions = expression.args.get(key or "expressions") if expression else sqls
3090
3091        if not expressions:
3092            return ""
3093
3094        if flat:
3095            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
3096
3097        num_sqls = len(expressions)
3098
3099        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
3100        pad = " " * self.pad
3101        stripped_sep = sep.strip()
3102
3103        result_sqls = []
3104        for i, e in enumerate(expressions):
3105            sql = self.sql(e, comment=False)
3106            if not sql:
3107                continue
3108
3109            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
3110
3111            if self.pretty:
3112                if self.leading_comma:
3113                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
3114                else:
3115                    result_sqls.append(
3116                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
3117                    )
3118            else:
3119                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
3120
3121        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
3122        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:
3124    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
3125        flat = flat or isinstance(expression.parent, exp.Properties)
3126        expressions_sql = self.expressions(expression, flat=flat)
3127        if flat:
3128            return f"{op} {expressions_sql}"
3129        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
def naked_property(self, expression: sqlglot.expressions.Property) -> str:
3131    def naked_property(self, expression: exp.Property) -> str:
3132        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
3133        if not property_name:
3134            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
3135        return f"{property_name} {self.sql(expression, 'this')}"
def set_operation(self, expression: sqlglot.expressions.Union, op: str) -> str:
3137    def set_operation(self, expression: exp.Union, op: str) -> str:
3138        this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments)
3139        op = self.seg(op)
3140        return self.query_modifiers(
3141            expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}"
3142        )
def tag_sql(self, expression: sqlglot.expressions.Tag) -> str:
3144    def tag_sql(self, expression: exp.Tag) -> str:
3145        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"
def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str:
3147    def token_sql(self, token_type: TokenType) -> str:
3148        return self.TOKEN_MAPPING.get(token_type, token_type.name)
def userdefinedfunction_sql(self, expression: sqlglot.expressions.UserDefinedFunction) -> str:
3150    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
3151        this = self.sql(expression, "this")
3152        expressions = self.no_identify(self.expressions, expression)
3153        expressions = (
3154            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
3155        )
3156        return f"{this}{expressions}"
def joinhint_sql(self, expression: sqlglot.expressions.JoinHint) -> str:
3158    def joinhint_sql(self, expression: exp.JoinHint) -> str:
3159        this = self.sql(expression, "this")
3160        expressions = self.expressions(expression, flat=True)
3161        return f"{this}({expressions})"
def kwarg_sql(self, expression: sqlglot.expressions.Kwarg) -> str:
3163    def kwarg_sql(self, expression: exp.Kwarg) -> str:
3164        return self.binary(expression, "=>")
def when_sql(self, expression: sqlglot.expressions.When) -> str:
3166    def when_sql(self, expression: exp.When) -> str:
3167        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
3168        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
3169        condition = self.sql(expression, "condition")
3170        condition = f" AND {condition}" if condition else ""
3171
3172        then_expression = expression.args.get("then")
3173        if isinstance(then_expression, exp.Insert):
3174            then = f"INSERT {self.sql(then_expression, 'this')}"
3175            if "expression" in then_expression.args:
3176                then += f" VALUES {self.sql(then_expression, 'expression')}"
3177        elif isinstance(then_expression, exp.Update):
3178            if isinstance(then_expression.args.get("expressions"), exp.Star):
3179                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
3180            else:
3181                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
3182        else:
3183            then = self.sql(then_expression)
3184        return f"WHEN {matched}{source}{condition} THEN {then}"
def merge_sql(self, expression: sqlglot.expressions.Merge) -> str:
3186    def merge_sql(self, expression: exp.Merge) -> str:
3187        table = expression.this
3188        table_alias = ""
3189
3190        hints = table.args.get("hints")
3191        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
3192            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
3193            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
3194
3195        this = self.sql(table)
3196        using = f"USING {self.sql(expression, 'using')}"
3197        on = f"ON {self.sql(expression, 'on')}"
3198        expressions = self.expressions(expression, sep=" ")
3199
3200        return self.prepend_ctes(
3201            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
3202        )
def tochar_sql(self, expression: sqlglot.expressions.ToChar) -> str:
3204    def tochar_sql(self, expression: exp.ToChar) -> str:
3205        if expression.args.get("format"):
3206            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
3207
3208        return self.sql(exp.cast(expression.this, "text"))
def dictproperty_sql(self, expression: sqlglot.expressions.DictProperty) -> str:
3210    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
3211        this = self.sql(expression, "this")
3212        kind = self.sql(expression, "kind")
3213        settings_sql = self.expressions(expression, key="settings", sep=" ")
3214        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
3215        return f"{this}({kind}{args})"
def dictrange_sql(self, expression: sqlglot.expressions.DictRange) -> str:
3217    def dictrange_sql(self, expression: exp.DictRange) -> str:
3218        this = self.sql(expression, "this")
3219        max = self.sql(expression, "max")
3220        min = self.sql(expression, "min")
3221        return f"{this}(MIN {min} MAX {max})"
def dictsubproperty_sql(self, expression: sqlglot.expressions.DictSubProperty) -> str:
3223    def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str:
3224        return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}"
def oncluster_sql(self, expression: sqlglot.expressions.OnCluster) -> str:
3226    def oncluster_sql(self, expression: exp.OnCluster) -> str:
3227        return ""
def clusteredbyproperty_sql(self, expression: sqlglot.expressions.ClusteredByProperty) -> str:
3229    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
3230        expressions = self.expressions(expression, key="expressions", flat=True)
3231        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
3232        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
3233        buckets = self.sql(expression, "buckets")
3234        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
def anyvalue_sql(self, expression: sqlglot.expressions.AnyValue) -> str:
3236    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
3237        this = self.sql(expression, "this")
3238        having = self.sql(expression, "having")
3239
3240        if having:
3241            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
3242
3243        return self.func("ANY_VALUE", this)
def querytransform_sql(self, expression: sqlglot.expressions.QueryTransform) -> str:
3245    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
3246        transform = self.func("TRANSFORM", *expression.expressions)
3247        row_format_before = self.sql(expression, "row_format_before")
3248        row_format_before = f" {row_format_before}" if row_format_before else ""
3249        record_writer = self.sql(expression, "record_writer")
3250        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
3251        using = f" USING {self.sql(expression, 'command_script')}"
3252        schema = self.sql(expression, "schema")
3253        schema = f" AS {schema}" if schema else ""
3254        row_format_after = self.sql(expression, "row_format_after")
3255        row_format_after = f" {row_format_after}" if row_format_after else ""
3256        record_reader = self.sql(expression, "record_reader")
3257        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
3258        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
def indexconstraintoption_sql(self, expression: sqlglot.expressions.IndexConstraintOption) -> str:
3260    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
3261        key_block_size = self.sql(expression, "key_block_size")
3262        if key_block_size:
3263            return f"KEY_BLOCK_SIZE = {key_block_size}"
3264
3265        using = self.sql(expression, "using")
3266        if using:
3267            return f"USING {using}"
3268
3269        parser = self.sql(expression, "parser")
3270        if parser:
3271            return f"WITH PARSER {parser}"
3272
3273        comment = self.sql(expression, "comment")
3274        if comment:
3275            return f"COMMENT {comment}"
3276
3277        visible = expression.args.get("visible")
3278        if visible is not None:
3279            return "VISIBLE" if visible else "INVISIBLE"
3280
3281        engine_attr = self.sql(expression, "engine_attr")
3282        if engine_attr:
3283            return f"ENGINE_ATTRIBUTE = {engine_attr}"
3284
3285        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
3286        if secondary_engine_attr:
3287            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
3288
3289        self.unsupported("Unsupported index constraint option.")
3290        return ""
def checkcolumnconstraint_sql(self, expression: sqlglot.expressions.CheckColumnConstraint) -> str:
3292    def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str:
3293        enforced = " ENFORCED" if expression.args.get("enforced") else ""
3294        return f"CHECK ({self.sql(expression, 'this')}){enforced}"
def indexcolumnconstraint_sql(self, expression: sqlglot.expressions.IndexColumnConstraint) -> str:
3296    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
3297        kind = self.sql(expression, "kind")
3298        kind = f"{kind} INDEX" if kind else "INDEX"
3299        this = self.sql(expression, "this")
3300        this = f" {this}" if this else ""
3301        index_type = self.sql(expression, "index_type")
3302        index_type = f" USING {index_type}" if index_type else ""
3303        schema = self.sql(expression, "schema")
3304        schema = f" {schema}" if schema else ""
3305        options = self.expressions(expression, key="options", sep=" ")
3306        options = f" {options}" if options else ""
3307        return f"{kind}{this}{index_type}{schema}{options}"
def nvl2_sql(self, expression: sqlglot.expressions.Nvl2) -> str:
3309    def nvl2_sql(self, expression: exp.Nvl2) -> str:
3310        if self.NVL2_SUPPORTED:
3311            return self.function_fallback_sql(expression)
3312
3313        case = exp.Case().when(
3314            expression.this.is_(exp.null()).not_(copy=False),
3315            expression.args["true"],
3316            copy=False,
3317        )
3318        else_cond = expression.args.get("false")
3319        if else_cond:
3320            case.else_(else_cond, copy=False)
3321
3322        return self.sql(case)
def comprehension_sql(self, expression: sqlglot.expressions.Comprehension) -> str:
3324    def comprehension_sql(self, expression: exp.Comprehension) -> str:
3325        this = self.sql(expression, "this")
3326        expr = self.sql(expression, "expression")
3327        iterator = self.sql(expression, "iterator")
3328        condition = self.sql(expression, "condition")
3329        condition = f" IF {condition}" if condition else ""
3330        return f"{this} FOR {expr} IN {iterator}{condition}"
def columnprefix_sql(self, expression: sqlglot.expressions.ColumnPrefix) -> str:
3332    def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str:
3333        return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})"
def opclass_sql(self, expression: sqlglot.expressions.Opclass) -> str:
3335    def opclass_sql(self, expression: exp.Opclass) -> str:
3336        return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}"
def predict_sql(self, expression: sqlglot.expressions.Predict) -> str:
3338    def predict_sql(self, expression: exp.Predict) -> str:
3339        model = self.sql(expression, "this")
3340        model = f"MODEL {model}"
3341        table = self.sql(expression, "expression")
3342        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
3343        parameters = self.sql(expression, "params_struct")
3344        return self.func("PREDICT", model, table, parameters or None)
def forin_sql(self, expression: sqlglot.expressions.ForIn) -> str:
3346    def forin_sql(self, expression: exp.ForIn) -> str:
3347        this = self.sql(expression, "this")
3348        expression_sql = self.sql(expression, "expression")
3349        return f"FOR {this} DO {expression_sql}"
def refresh_sql(self, expression: sqlglot.expressions.Refresh) -> str:
3351    def refresh_sql(self, expression: exp.Refresh) -> str:
3352        this = self.sql(expression, "this")
3353        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
3354        return f"REFRESH {table}{this}"
def operator_sql(self, expression: sqlglot.expressions.Operator) -> str:
3356    def operator_sql(self, expression: exp.Operator) -> str:
3357        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")
def toarray_sql(self, expression: sqlglot.expressions.ToArray) -> str:
3359    def toarray_sql(self, expression: exp.ToArray) -> str:
3360        arg = expression.this
3361        if not arg.type:
3362            from sqlglot.optimizer.annotate_types import annotate_types
3363
3364            arg = annotate_types(arg)
3365
3366        if arg.is_type(exp.DataType.Type.ARRAY):
3367            return self.sql(arg)
3368
3369        cond_for_null = arg.is_(exp.null())
3370        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:
3372    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
3373        this = expression.this
3374        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
3375            return self.sql(this)
3376
3377        return self.sql(exp.cast(this, "time"))
def tsordstodate_sql(self, expression: sqlglot.expressions.TsOrDsToDate) -> str:
3379    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
3380        this = expression.this
3381        time_format = self.format_time(expression)
3382
3383        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
3384            return self.sql(
3385                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
3386            )
3387
3388        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
3389            return self.sql(this)
3390
3391        return self.sql(exp.cast(this, "date"))
def unixdate_sql(self, expression: sqlglot.expressions.UnixDate) -> str:
3393    def unixdate_sql(self, expression: exp.UnixDate) -> str:
3394        return self.sql(
3395            exp.func(
3396                "DATEDIFF",
3397                expression.this,
3398                exp.cast(exp.Literal.string("1970-01-01"), "date"),
3399                "day",
3400            )
3401        )
def lastday_sql(self, expression: sqlglot.expressions.LastDay) -> str:
3403    def lastday_sql(self, expression: exp.LastDay) -> str:
3404        if self.LAST_DAY_SUPPORTS_DATE_PART:
3405            return self.function_fallback_sql(expression)
3406
3407        unit = expression.text("unit")
3408        if unit and unit != "MONTH":
3409            self.unsupported("Date parts are not supported in LAST_DAY.")
3410
3411        return self.func("LAST_DAY", expression.this)
def arrayany_sql(self, expression: sqlglot.expressions.ArrayAny) -> str:
3413    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
3414        if self.CAN_IMPLEMENT_ARRAY_ANY:
3415            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
3416            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
3417            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
3418            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
3419
3420        from sqlglot.dialects import Dialect
3421
3422        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
3423        if self.dialect.__class__ != Dialect:
3424            self.unsupported("ARRAY_ANY is unsupported")
3425
3426        return self.function_fallback_sql(expression)