sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

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

logger = <Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE = re.compile('\\\\(\\d+)')
class Generator: View Source

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

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

###### Arguments:

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

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

    558    def __init__(
    559        self,
    560        pretty: t.Optional[bool] = None,
    561        identify: str | bool = False,
    562        normalize: bool = False,
    563        pad: int = 2,
    564        indent: int = 2,
    565        normalize_functions: t.Optional[str | bool] = None,
    566        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    567        max_unsupported: int = 3,
    568        leading_comma: bool = False,
    569        max_text_width: int = 80,
    570        comments: bool = True,
    571        dialect: DialectType = None,
    572    ):
    573        import sqlglot
    574        from sqlglot.dialects import Dialect
    575
    576        self.pretty = pretty if pretty is not None else sqlglot.pretty
    577        self.identify = identify
    578        self.normalize = normalize
    579        self.pad = pad
    580        self._indent = indent
    581        self.unsupported_level = unsupported_level
    582        self.max_unsupported = max_unsupported
    583        self.leading_comma = leading_comma
    584        self.max_text_width = max_text_width
    585        self.comments = comments
    586        self.dialect = Dialect.get_or_raise(dialect)
    587
    588        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    589        self.normalize_functions = (
    590            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    591        )
    592
    593        self.unsupported_messages: t.List[str] = []
    594        self._escaped_quote_end: str = (
    595            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    596        )
    597        self._escaped_identifier_end: str = (
    598            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
    599        )
    600
    601        self._next_name = name_sequence("_t")

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <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.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <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.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <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
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
OUTER_UNION_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
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', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.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.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StrictProperty'>: <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.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Union'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.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'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.VARCHAR: 'VARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

    603    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    604        """
    605        Generates the SQL string corresponding to the given syntax tree.
    606
    607        Args:
    608            expression: The syntax tree.
    609            copy: Whether to copy the expression. The generator performs mutations so
    610                it is safer to copy.
    611
    612        Returns:
    613            The SQL string corresponding to `expression`.
    614        """
    615        if copy:
    616            expression = expression.copy()
    617
    618        expression = self.preprocess(expression)
    619
    620        self.unsupported_messages = []
    621        sql = self.sql(expression).strip()
    622
    623        if self.pretty:
    624            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    625
    626        if self.unsupported_level == ErrorLevel.IGNORE:
    627            return sql
    628
    629        if self.unsupported_level == ErrorLevel.WARN:
    630            for msg in self.unsupported_messages:
    631                logger.warning(msg)
    632        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    633            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    634
    635        return sql

Generates the SQL string corresponding to the given syntax tree.

###### Arguments:

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

###### Returns:

> The SQL string corresponding to `expression`.

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

    637    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    638        """Apply generic preprocessing transformations to a given expression."""
    639        if (
    640            not expression.parent
    641            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    642            and any(node.parent is not expression for node in expression.find_all(exp.With))
    643        ):
    644            from sqlglot.transforms import move_ctes_to_top_level
    645
    646            expression = move_ctes_to_top_level(expression)
    647
    648        if self.ENSURE_BOOLS:
    649            from sqlglot.transforms import ensure_bools
    650
    651            expression = ensure_bools(expression)
    652
    653        return expression

Apply generic preprocessing transformations to a given expression.

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

    655    def unsupported(self, message: str) -> None:
    656        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    657            raise UnsupportedError(message)
    658        self.unsupported_messages.append(message)

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

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

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

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

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

    666    def pad_comment(self, comment: str) -> str:
    667        comment = " " + comment if comment[0].strip() else comment
    668        comment = comment + " " if comment[-1].strip() else comment
    669        return comment

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

    671    def maybe_comment(
    672        self,
    673        sql: str,
    674        expression: t.Optional[exp.Expression] = None,
    675        comments: t.Optional[t.List[str]] = None,
    676        separated: bool = False,
    677    ) -> str:
    678        comments = (
    679            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    680            if self.comments
    681            else None
    682        )
    683
    684        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    685            return sql
    686
    687        comments_sql = " ".join(
    688            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    689        )
    690
    691        if not comments_sql:
    692            return sql
    693
    694        comments_sql = self._replace_line_breaks(comments_sql)
    695
    696        if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    697            return (
    698                f"{self.sep()}{comments_sql}{sql}"
    699                if not sql or sql[0].isspace()
    700                else f"{comments_sql}{self.sep()}{sql}"
    701            )
    702
    703        return f"{sql} {comments_sql}"

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

    705    def wrap(self, expression: exp.Expression | str) -> str:
    706        this_sql = (
    707            self.sql(expression)
    708            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    709            else self.sql(expression, "this")
    710        )
    711        if not this_sql:
    712            return "()"
    713
    714        this_sql = self.indent(this_sql, level=1, pad=0)
    715        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    717    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    718        original = self.identify
    719        self.identify = False
    720        result = func(*args, **kwargs)
    721        self.identify = original
    722        return result

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

    724    def normalize_func(self, name: str) -> str:
    725        if self.normalize_functions == "upper" or self.normalize_functions is True:
    726            return name.upper()
    727        if self.normalize_functions == "lower":
    728            return name.lower()
    729        return name

def indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str: View Source

    731    def indent(
    732        self,
    733        sql: str,
    734        level: int = 0,
    735        pad: t.Optional[int] = None,
    736        skip_first: bool = False,
    737        skip_last: bool = False,
    738    ) -> str:
    739        if not self.pretty or not sql:
    740            return sql
    741
    742        pad = self.pad if pad is None else pad
    743        lines = sql.split("\n")
    744
    745        return "\n".join(
    746            (
    747                line
    748                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    749                else f"{' ' * (level * self._indent + pad)}{line}"
    750            )
    751            for i, line in enumerate(lines)
    752        )

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

    754    def sql(
    755        self,
    756        expression: t.Optional[str | exp.Expression],
    757        key: t.Optional[str] = None,
    758        comment: bool = True,
    759    ) -> str:
    760        if not expression:
    761            return ""
    762
    763        if isinstance(expression, str):
    764            return expression
    765
    766        if key:
    767            value = expression.args.get(key)
    768            if value:
    769                return self.sql(value)
    770            return ""
    771
    772        transform = self.TRANSFORMS.get(expression.__class__)
    773
    774        if callable(transform):
    775            sql = transform(self, expression)
    776        elif isinstance(expression, exp.Expression):
    777            exp_handler_name = f"{expression.key}_sql"
    778
    779            if hasattr(self, exp_handler_name):
    780                sql = getattr(self, exp_handler_name)(expression)
    781            elif isinstance(expression, exp.Func):
    782                sql = self.function_fallback_sql(expression)
    783            elif isinstance(expression, exp.Property):
    784                sql = self.property_sql(expression)
    785            else:
    786                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    787        else:
    788            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    789
    790        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

    797    def cache_sql(self, expression: exp.Cache) -> str:
    798        lazy = " LAZY" if expression.args.get("lazy") else ""
    799        table = self.sql(expression, "this")
    800        options = expression.args.get("options")
    801        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    802        sql = self.sql(expression, "expression")
    803        sql = f" AS{self.sep()}{sql}" if sql else ""
    804        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    805        return self.prepend_ctes(expression, sql)

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

    807    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    808        if isinstance(expression.parent, exp.Cast):
    809            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    810        default = "DEFAULT " if expression.args.get("default") else ""
    811        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    813    def column_parts(self, expression: exp.Column) -> str:
    814        return ".".join(
    815            self.sql(part)
    816            for part in (
    817                expression.args.get("catalog"),
    818                expression.args.get("db"),
    819                expression.args.get("table"),
    820                expression.args.get("this"),
    821            )
    822            if part
    823        )

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

    825    def column_sql(self, expression: exp.Column) -> str:
    826        join_mark = " (+)" if expression.args.get("join_mark") else ""
    827
    828        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
    829            join_mark = ""
    830            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    831
    832        return f"{self.column_parts(expression)}{join_mark}"

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

    834    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    835        this = self.sql(expression, "this")
    836        this = f" {this}" if this else ""
    837        position = self.sql(expression, "position")
    838        return f"{position}{this}"

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

    840    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    841        column = self.sql(expression, "this")
    842        kind = self.sql(expression, "kind")
    843        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    844        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    845        kind = f"{sep}{kind}" if kind else ""
    846        constraints = f" {constraints}" if constraints else ""
    847        position = self.sql(expression, "position")
    848        position = f" {position}" if position else ""
    849
    850        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    851            kind = ""
    852
    853        return f"{exists}{column}{kind}{constraints}{position}"

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

    855    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    856        this = self.sql(expression, "this")
    857        kind_sql = self.sql(expression, "kind").strip()
    858        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    860    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    861        this = self.sql(expression, "this")
    862        if expression.args.get("not_null"):
    863            persisted = " PERSISTED NOT NULL"
    864        elif expression.args.get("persisted"):
    865            persisted = " PERSISTED"
    866        else:
    867            persisted = ""
    868        return f"AS {this}{persisted}"

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

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

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

    873    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    874        if isinstance(expression.this, list):
    875            this = self.wrap(self.expressions(expression, key="this", flat=True))
    876        else:
    877            this = self.sql(expression, "this")
    878
    879        return f"COMPRESS {this}"

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

    881    def generatedasidentitycolumnconstraint_sql(
    882        self, expression: exp.GeneratedAsIdentityColumnConstraint
    883    ) -> str:
    884        this = ""
    885        if expression.this is not None:
    886            on_null = " ON NULL" if expression.args.get("on_null") else ""
    887            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    888
    889        start = expression.args.get("start")
    890        start = f"START WITH {start}" if start else ""
    891        increment = expression.args.get("increment")
    892        increment = f" INCREMENT BY {increment}" if increment else ""
    893        minvalue = expression.args.get("minvalue")
    894        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    895        maxvalue = expression.args.get("maxvalue")
    896        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    897        cycle = expression.args.get("cycle")
    898        cycle_sql = ""
    899
    900        if cycle is not None:
    901            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    902            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    903
    904        sequence_opts = ""
    905        if start or increment or cycle_sql:
    906            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    907            sequence_opts = f" ({sequence_opts.strip()})"
    908
    909        expr = self.sql(expression, "expression")
    910        expr = f"({expr})" if expr else "IDENTITY"
    911
    912        return f"GENERATED{this} AS {expr}{sequence_opts}"

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

    914    def generatedasrowcolumnconstraint_sql(
    915        self, expression: exp.GeneratedAsRowColumnConstraint
    916    ) -> str:
    917        start = "START" if expression.args.get("start") else "END"
    918        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    919        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    921    def periodforsystemtimeconstraint_sql(
    922        self, expression: exp.PeriodForSystemTimeConstraint
    923    ) -> str:
    924        return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})"

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

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

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

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

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

    932    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    933        desc = expression.args.get("desc")
    934        if desc is not None:
    935            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    936        return "PRIMARY KEY"

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

    938    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    939        this = self.sql(expression, "this")
    940        this = f" {this}" if this else ""
    941        index_type = expression.args.get("index_type")
    942        index_type = f" USING {index_type}" if index_type else ""
    943        on_conflict = self.sql(expression, "on_conflict")
    944        on_conflict = f" {on_conflict}" if on_conflict else ""
    945        return f"UNIQUE{this}{index_type}{on_conflict}"

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

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

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

     950    def create_sql(self, expression: exp.Create) -> str:
     951        kind = self.sql(expression, "kind")
     952        properties = expression.args.get("properties")
     953        properties_locs = self.locate_properties(properties) if properties else defaultdict()
     954
     955        this = self.createable_sql(expression, properties_locs)
     956
     957        properties_sql = ""
     958        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
     959            exp.Properties.Location.POST_WITH
     960        ):
     961            properties_sql = self.sql(
     962                exp.Properties(
     963                    expressions=[
     964                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
     965                        *properties_locs[exp.Properties.Location.POST_WITH],
     966                    ]
     967                )
     968            )
     969
     970            if properties_locs.get(exp.Properties.Location.POST_SCHEMA):
     971                properties_sql = self.sep() + properties_sql
     972            elif not self.pretty:
     973                # Standalone POST_WITH properties need a leading whitespace in non-pretty mode
     974                properties_sql = f" {properties_sql}"
     975
     976        begin = " BEGIN" if expression.args.get("begin") else ""
     977        end = " END" if expression.args.get("end") else ""
     978
     979        expression_sql = self.sql(expression, "expression")
     980        if expression_sql:
     981            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
     982
     983            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
     984                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
     985                    postalias_props_sql = self.properties(
     986                        exp.Properties(
     987                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
     988                        ),
     989                        wrapped=False,
     990                    )
     991                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
     992                else:
     993                    expression_sql = f" AS{expression_sql}"
     994
     995        postindex_props_sql = ""
     996        if properties_locs.get(exp.Properties.Location.POST_INDEX):
     997            postindex_props_sql = self.properties(
     998                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
     999                wrapped=False,
    1000                prefix=" ",
    1001            )
    1002
    1003        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    1004        indexes = f" {indexes}" if indexes else ""
    1005        index_sql = indexes + postindex_props_sql
    1006
    1007        replace = " OR REPLACE" if expression.args.get("replace") else ""
    1008        unique = " UNIQUE" if expression.args.get("unique") else ""
    1009
    1010        postcreate_props_sql = ""
    1011        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    1012            postcreate_props_sql = self.properties(
    1013                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    1014                sep=" ",
    1015                prefix=" ",
    1016                wrapped=False,
    1017            )
    1018
    1019        modifiers = "".join((replace, unique, postcreate_props_sql))
    1020
    1021        postexpression_props_sql = ""
    1022        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    1023            postexpression_props_sql = self.properties(
    1024                exp.Properties(
    1025                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    1026                ),
    1027                sep=" ",
    1028                prefix=" ",
    1029                wrapped=False,
    1030            )
    1031
    1032        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    1033        no_schema_binding = (
    1034            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    1035        )
    1036
    1037        clone = self.sql(expression, "clone")
    1038        clone = f" {clone}" if clone else ""
    1039
    1040        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    1041        return self.prepend_ctes(expression, expression_sql)

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

    1043    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
    1044        start = self.sql(expression, "start")
    1045        start = f"START WITH {start}" if start else ""
    1046        increment = self.sql(expression, "increment")
    1047        increment = f" INCREMENT BY {increment}" if increment else ""
    1048        minvalue = self.sql(expression, "minvalue")
    1049        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1050        maxvalue = self.sql(expression, "maxvalue")
    1051        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1052        owned = self.sql(expression, "owned")
    1053        owned = f" OWNED BY {owned}" if owned else ""
    1054
    1055        cache = expression.args.get("cache")
    1056        if cache is None:
    1057            cache_str = ""
    1058        elif cache is True:
    1059            cache_str = " CACHE"
    1060        else:
    1061            cache_str = f" CACHE {cache}"
    1062
    1063        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1064        options = f" {options}" if options else ""
    1065
    1066        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

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

    1068    def clone_sql(self, expression: exp.Clone) -> str:
    1069        this = self.sql(expression, "this")
    1070        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1071        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1072        return f"{shallow}{keyword} {this}"

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

    1074    def describe_sql(self, expression: exp.Describe) -> str:
    1075        style = expression.args.get("style")
    1076        style = f" {style}" if style else ""
    1077        return f"DESCRIBE{style} {self.sql(expression, 'this')}"

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

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

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

    1083    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1084        with_ = self.sql(expression, "with")
    1085        if with_:
    1086            sql = f"{with_}{self.sep()}{sql}"
    1087        return sql

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

    1089    def with_sql(self, expression: exp.With) -> str:
    1090        sql = self.expressions(expression, flat=True)
    1091        recursive = (
    1092            "RECURSIVE "
    1093            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1094            else ""
    1095        )
    1096
    1097        return f"WITH {recursive}{sql}"

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

    1099    def cte_sql(self, expression: exp.CTE) -> str:
    1100        alias = self.sql(expression, "alias")
    1101
    1102        materialized = expression.args.get("materialized")
    1103        if materialized is False:
    1104            materialized = "NOT MATERIALIZED "
    1105        elif materialized:
    1106            materialized = "MATERIALIZED "
    1107
    1108        return f"{alias} AS {materialized or ''}{self.wrap(expression)}"

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

    1110    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1111        alias = self.sql(expression, "this")
    1112        columns = self.expressions(expression, key="columns", flat=True)
    1113        columns = f"({columns})" if columns else ""
    1114
    1115        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1116            columns = ""
    1117            self.unsupported("Named columns are not supported in table alias.")
    1118
    1119        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1120            alias = self._next_name()
    1121
    1122        return f"{alias}{columns}"

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

    1124    def bitstring_sql(self, expression: exp.BitString) -> str:
    1125        this = self.sql(expression, "this")
    1126        if self.dialect.BIT_START:
    1127            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1128        return f"{int(this, 2)}"

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

    1130    def hexstring_sql(self, expression: exp.HexString) -> str:
    1131        this = self.sql(expression, "this")
    1132        if self.dialect.HEX_START:
    1133            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1134        return f"{int(this, 16)}"

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

    1136    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1137        this = self.sql(expression, "this")
    1138        if self.dialect.BYTE_START:
    1139            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1140        return this

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

    1142    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1143        this = self.sql(expression, "this")
    1144        escape = expression.args.get("escape")
    1145
    1146        if self.dialect.UNICODE_START:
    1147            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
    1148            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
    1149
    1150        if escape:
    1151            pattern = re.compile(rf"{escape.name}(\d+)")
    1152        else:
    1153            pattern = ESCAPED_UNICODE_RE
    1154
    1155        this = pattern.sub(r"\\u\1", this)
    1156        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"

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

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

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

    1162    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1163        this = self.sql(expression, "this")
    1164        specifier = self.sql(expression, "expression")
    1165        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1166        return f"{this}{specifier}"

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

    1168    def datatype_sql(self, expression: exp.DataType) -> str:
    1169        type_value = expression.this
    1170
    1171        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1172            type_sql = self.sql(expression, "kind")
    1173        else:
    1174            type_sql = (
    1175                self.TYPE_MAPPING.get(type_value, type_value.value)
    1176                if isinstance(type_value, exp.DataType.Type)
    1177                else type_value
    1178            )
    1179
    1180        nested = ""
    1181        interior = self.expressions(expression, flat=True)
    1182        values = ""
    1183
    1184        if interior:
    1185            if expression.args.get("nested"):
    1186                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1187                if expression.args.get("values") is not None:
    1188                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1189                    values = self.expressions(expression, key="values", flat=True)
    1190                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1191            elif type_value == exp.DataType.Type.INTERVAL:
    1192                nested = f" {interior}"
    1193            else:
    1194                nested = f"({interior})"
    1195
    1196        type_sql = f"{type_sql}{nested}{values}"
    1197        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1198            exp.DataType.Type.TIMETZ,
    1199            exp.DataType.Type.TIMESTAMPTZ,
    1200        ):
    1201            type_sql = f"{type_sql} WITH TIME ZONE"
    1202
    1203        return type_sql

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

    1205    def directory_sql(self, expression: exp.Directory) -> str:
    1206        local = "LOCAL " if expression.args.get("local") else ""
    1207        row_format = self.sql(expression, "row_format")
    1208        row_format = f" {row_format}" if row_format else ""
    1209        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1211    def delete_sql(self, expression: exp.Delete) -> str:
    1212        this = self.sql(expression, "this")
    1213        this = f" FROM {this}" if this else ""
    1214        using = self.sql(expression, "using")
    1215        using = f" USING {using}" if using else ""
    1216        where = self.sql(expression, "where")
    1217        returning = self.sql(expression, "returning")
    1218        limit = self.sql(expression, "limit")
    1219        tables = self.expressions(expression, key="tables")
    1220        tables = f" {tables}" if tables else ""
    1221        if self.RETURNING_END:
    1222            expression_sql = f"{this}{using}{where}{returning}{limit}"
    1223        else:
    1224            expression_sql = f"{returning}{this}{using}{where}{limit}"
    1225        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1227    def drop_sql(self, expression: exp.Drop) -> str:
    1228        this = self.sql(expression, "this")
    1229        expressions = self.expressions(expression, flat=True)
    1230        expressions = f" ({expressions})" if expressions else ""
    1231        kind = expression.args["kind"]
    1232        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1233        on_cluster = self.sql(expression, "cluster")
    1234        on_cluster = f" {on_cluster}" if on_cluster else ""
    1235        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1236        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1237        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1238        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1239        purge = " PURGE" if expression.args.get("purge") else ""
    1240        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"

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

    1242    def except_sql(self, expression: exp.Except) -> str:
    1243        return self.set_operations(expression)

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

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

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

    1248    def fetch_sql(self, expression: exp.Fetch) -> str:
    1249        direction = expression.args.get("direction")
    1250        direction = f" {direction}" if direction else ""
    1251        count = expression.args.get("count")
    1252        count = f" {count}" if count else ""
    1253        if expression.args.get("percent"):
    1254            count = f"{count} PERCENT"
    1255        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1256        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1258    def filter_sql(self, expression: exp.Filter) -> str:
    1259        if self.AGGREGATE_FILTER_SUPPORTED:
    1260            this = self.sql(expression, "this")
    1261            where = self.sql(expression, "expression").strip()
    1262            return f"{this} FILTER({where})"
    1263
    1264        agg = expression.this
    1265        agg_arg = agg.this
    1266        cond = expression.expression.this
    1267        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1268        return self.sql(agg)

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

    1270    def hint_sql(self, expression: exp.Hint) -> str:
    1271        if not self.QUERY_HINTS:
    1272            self.unsupported("Hints are not supported")
    1273            return ""
    1274
    1275        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1277    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1278        using = self.sql(expression, "using")
    1279        using = f" USING {using}" if using else ""
    1280        columns = self.expressions(expression, key="columns", flat=True)
    1281        columns = f"({columns})" if columns else ""
    1282        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1283        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1284        where = self.sql(expression, "where")
    1285        include = self.expressions(expression, key="include", flat=True)
    1286        if include:
    1287            include = f" INCLUDE ({include})"
    1288        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1289        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1290        tablespace = self.sql(expression, "tablespace")
    1291        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1292
    1293        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}"

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

    1295    def index_sql(self, expression: exp.Index) -> str:
    1296        unique = "UNIQUE " if expression.args.get("unique") else ""
    1297        primary = "PRIMARY " if expression.args.get("primary") else ""
    1298        amp = "AMP " if expression.args.get("amp") else ""
    1299        name = self.sql(expression, "this")
    1300        name = f"{name} " if name else ""
    1301        table = self.sql(expression, "table")
    1302        table = f"{self.INDEX_ON} {table}" if table else ""
    1303
    1304        index = "INDEX " if not table else ""
    1305
    1306        params = self.sql(expression, "params")
    1307        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1309    def identifier_sql(self, expression: exp.Identifier) -> str:
    1310        text = expression.name
    1311        lower = text.lower()
    1312        text = lower if self.normalize and not expression.quoted else text
    1313        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1314        if (
    1315            expression.quoted
    1316            or self.dialect.can_identify(text, self.identify)
    1317            or lower in self.RESERVED_KEYWORDS
    1318            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1319        ):
    1320            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1321        return text

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

    1323    def hex_sql(self, expression: exp.Hex) -> str:
    1324        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1325        if self.dialect.HEX_LOWERCASE:
    1326            text = self.func("LOWER", text)
    1327
    1328        return text

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

    1330    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1331        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1332        if not self.dialect.HEX_LOWERCASE:
    1333            text = self.func("LOWER", text)
    1334        return text

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

    1336    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1337        input_format = self.sql(expression, "input_format")
    1338        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1339        output_format = self.sql(expression, "output_format")
    1340        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1341        return self.sep().join((input_format, output_format))

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

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

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

    1347    def partition_sql(self, expression: exp.Partition) -> str:
    1348        return f"PARTITION({self.expressions(expression, flat=True)})"

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

    1350    def properties_sql(self, expression: exp.Properties) -> str:
    1351        root_properties = []
    1352        with_properties = []
    1353
    1354        for p in expression.expressions:
    1355            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1356            if p_loc == exp.Properties.Location.POST_WITH:
    1357                with_properties.append(p)
    1358            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1359                root_properties.append(p)
    1360
    1361        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1362        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1363
    1364        if root_props and with_props and not self.pretty:
    1365            with_props = " " + with_props
    1366
    1367        return root_props + with_props

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

    1369    def root_properties(self, properties: exp.Properties) -> str:
    1370        if properties.expressions:
    1371            return self.expressions(properties, indent=False, sep=" ")
    1372        return ""

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

    1374    def properties(
    1375        self,
    1376        properties: exp.Properties,
    1377        prefix: str = "",
    1378        sep: str = ", ",
    1379        suffix: str = "",
    1380        wrapped: bool = True,
    1381    ) -> str:
    1382        if properties.expressions:
    1383            expressions = self.expressions(properties, sep=sep, indent=False)
    1384            if expressions:
    1385                expressions = self.wrap(expressions) if wrapped else expressions
    1386                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1387        return ""

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

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

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

    1392    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1393        properties_locs = defaultdict(list)
    1394        for p in properties.expressions:
    1395            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1396            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1397                properties_locs[p_loc].append(p)
    1398            else:
    1399                self.unsupported(f"Unsupported property {p.key}")
    1400
    1401        return properties_locs

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

    1403    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1404        if isinstance(expression.this, exp.Dot):
    1405            return self.sql(expression, "this")
    1406        return f"'{expression.name}'" if string_key else expression.name

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

    1408    def property_sql(self, expression: exp.Property) -> str:
    1409        property_cls = expression.__class__
    1410        if property_cls == exp.Property:
    1411            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1412
    1413        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1414        if not property_name:
    1415            self.unsupported(f"Unsupported property {expression.key}")
    1416
    1417        return f"{property_name}={self.sql(expression, 'this')}"

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

    1419    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1420        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1421            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1422            options = f" {options}" if options else ""
    1423
    1424            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1425            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1426                like = f"({like})"
    1427
    1428            return like
    1429
    1430        if expression.expressions:
    1431            self.unsupported("Transpilation of LIKE property options is unsupported")
    1432
    1433        select = exp.select("*").from_(expression.this).limit(0)
    1434        return f"AS {self.sql(select)}"

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

    1436    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1437        no = "NO " if expression.args.get("no") else ""
    1438        protection = " PROTECTION" if expression.args.get("protection") else ""
    1439        return f"{no}FALLBACK{protection}"

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

    1441    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1442        no = "NO " if expression.args.get("no") else ""
    1443        local = expression.args.get("local")
    1444        local = f"{local} " if local else ""
    1445        dual = "DUAL " if expression.args.get("dual") else ""
    1446        before = "BEFORE " if expression.args.get("before") else ""
    1447        after = "AFTER " if expression.args.get("after") else ""
    1448        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1450    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1451        freespace = self.sql(expression, "this")
    1452        percent = " PERCENT" if expression.args.get("percent") else ""
    1453        return f"FREESPACE={freespace}{percent}"

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

    1455    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1456        if expression.args.get("default"):
    1457            property = "DEFAULT"
    1458        elif expression.args.get("on"):
    1459            property = "ON"
    1460        else:
    1461            property = "OFF"
    1462        return f"CHECKSUM={property}"

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

    1464    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1465        if expression.args.get("no"):
    1466            return "NO MERGEBLOCKRATIO"
    1467        if expression.args.get("default"):
    1468            return "DEFAULT MERGEBLOCKRATIO"
    1469
    1470        percent = " PERCENT" if expression.args.get("percent") else ""
    1471        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1473    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1474        default = expression.args.get("default")
    1475        minimum = expression.args.get("minimum")
    1476        maximum = expression.args.get("maximum")
    1477        if default or minimum or maximum:
    1478            if default:
    1479                prop = "DEFAULT"
    1480            elif minimum:
    1481                prop = "MINIMUM"
    1482            else:
    1483                prop = "MAXIMUM"
    1484            return f"{prop} DATABLOCKSIZE"
    1485        units = expression.args.get("units")
    1486        units = f" {units}" if units else ""
    1487        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1489    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1490        autotemp = expression.args.get("autotemp")
    1491        always = expression.args.get("always")
    1492        default = expression.args.get("default")
    1493        manual = expression.args.get("manual")
    1494        never = expression.args.get("never")
    1495
    1496        if autotemp is not None:
    1497            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1498        elif always:
    1499            prop = "ALWAYS"
    1500        elif default:
    1501            prop = "DEFAULT"
    1502        elif manual:
    1503            prop = "MANUAL"
    1504        elif never:
    1505            prop = "NEVER"
    1506        return f"BLOCKCOMPRESSION={prop}"

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

    1508    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1509        no = expression.args.get("no")
    1510        no = " NO" if no else ""
    1511        concurrent = expression.args.get("concurrent")
    1512        concurrent = " CONCURRENT" if concurrent else ""
    1513        target = self.sql(expression, "target")
    1514        target = f" {target}" if target else ""
    1515        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1517    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1518        if isinstance(expression.this, list):
    1519            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1520        if expression.this:
    1521            modulus = self.sql(expression, "this")
    1522            remainder = self.sql(expression, "expression")
    1523            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1524
    1525        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1526        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1527        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1529    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1530        this = self.sql(expression, "this")
    1531
    1532        for_values_or_default = expression.expression
    1533        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1534            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1535        else:
    1536            for_values_or_default = " DEFAULT"
    1537
    1538        return f"PARTITION OF {this}{for_values_or_default}"

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

    1540    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1541        kind = expression.args.get("kind")
    1542        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1543        for_or_in = expression.args.get("for_or_in")
    1544        for_or_in = f" {for_or_in}" if for_or_in else ""
    1545        lock_type = expression.args.get("lock_type")
    1546        override = " OVERRIDE" if expression.args.get("override") else ""
    1547        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1549    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1550        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1551        statistics = expression.args.get("statistics")
    1552        statistics_sql = ""
    1553        if statistics is not None:
    1554            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1555        return f"{data_sql}{statistics_sql}"

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

    1557    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1558        this = self.sql(expression, "this")
    1559        this = f"HISTORY_TABLE={this}" if this else ""
    1560        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1561        data_consistency = (
    1562            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1563        )
    1564        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1565        retention_period = (
    1566            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1567        )
    1568
    1569        if this:
    1570            on_sql = self.func("ON", this, data_consistency, retention_period)
    1571        else:
    1572            on_sql = "ON" if expression.args.get("on") else "OFF"
    1573
    1574        sql = f"SYSTEM_VERSIONING={on_sql}"
    1575
    1576        return f"WITH({sql})" if expression.args.get("with") else sql

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

    1578    def insert_sql(self, expression: exp.Insert) -> str:
    1579        hint = self.sql(expression, "hint")
    1580        overwrite = expression.args.get("overwrite")
    1581
    1582        if isinstance(expression.this, exp.Directory):
    1583            this = " OVERWRITE" if overwrite else " INTO"
    1584        else:
    1585            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1586
    1587        stored = self.sql(expression, "stored")
    1588        stored = f" {stored}" if stored else ""
    1589        alternative = expression.args.get("alternative")
    1590        alternative = f" OR {alternative}" if alternative else ""
    1591        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1592        is_function = expression.args.get("is_function")
    1593        if is_function:
    1594            this = f"{this} FUNCTION"
    1595        this = f"{this} {self.sql(expression, 'this')}"
    1596
    1597        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1598        where = self.sql(expression, "where")
    1599        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1600        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1601        on_conflict = self.sql(expression, "conflict")
    1602        on_conflict = f" {on_conflict}" if on_conflict else ""
    1603        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1604        returning = self.sql(expression, "returning")
    1605
    1606        if self.RETURNING_END:
    1607            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1608        else:
    1609            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1610
    1611        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}"
    1612        return self.prepend_ctes(expression, sql)

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

    1614    def intersect_sql(self, expression: exp.Intersect) -> str:
    1615        return self.set_operations(expression)

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

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

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

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

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

    1623    def kill_sql(self, expression: exp.Kill) -> str:
    1624        kind = self.sql(expression, "kind")
    1625        kind = f" {kind}" if kind else ""
    1626        this = self.sql(expression, "this")
    1627        this = f" {this}" if this else ""
    1628        return f"KILL{kind}{this}"

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

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

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

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

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

    1636    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1637        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1638
    1639        constraint = self.sql(expression, "constraint")
    1640        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1641
    1642        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1643        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1644        action = self.sql(expression, "action")
    1645
    1646        expressions = self.expressions(expression, flat=True)
    1647        if expressions:
    1648            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1649            expressions = f" {set_keyword}{expressions}"
    1650
    1651        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"

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

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

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

    1656    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1657        fields = self.sql(expression, "fields")
    1658        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1659        escaped = self.sql(expression, "escaped")
    1660        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1661        items = self.sql(expression, "collection_items")
    1662        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1663        keys = self.sql(expression, "map_keys")
    1664        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1665        lines = self.sql(expression, "lines")
    1666        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1667        null = self.sql(expression, "null")
    1668        null = f" NULL DEFINED AS {null}" if null else ""
    1669        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1674    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1675        this = f"{self.sql(expression, 'this')} INDEX"
    1676        target = self.sql(expression, "target")
    1677        target = f" FOR {target}" if target else ""
    1678        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1680    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1681        this = self.sql(expression, "this")
    1682        kind = self.sql(expression, "kind")
    1683        expr = self.sql(expression, "expression")
    1684        return f"{this} ({kind} => {expr})"

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

    1686    def table_parts(self, expression: exp.Table) -> str:
    1687        return ".".join(
    1688            self.sql(part)
    1689            for part in (
    1690                expression.args.get("catalog"),
    1691                expression.args.get("db"),
    1692                expression.args.get("this"),
    1693            )
    1694            if part is not None
    1695        )

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

    1697    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1698        table = self.table_parts(expression)
    1699        only = "ONLY " if expression.args.get("only") else ""
    1700        partition = self.sql(expression, "partition")
    1701        partition = f" {partition}" if partition else ""
    1702        version = self.sql(expression, "version")
    1703        version = f" {version}" if version else ""
    1704        alias = self.sql(expression, "alias")
    1705        alias = f"{sep}{alias}" if alias else ""
    1706        hints = self.expressions(expression, key="hints", sep=" ")
    1707        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1708        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1709        pivots = f" {pivots}" if pivots else ""
    1710        joins = self.indent(
    1711            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1712        )
    1713        laterals = self.expressions(expression, key="laterals", sep="")
    1714
    1715        file_format = self.sql(expression, "format")
    1716        if file_format:
    1717            pattern = self.sql(expression, "pattern")
    1718            pattern = f", PATTERN => {pattern}" if pattern else ""
    1719            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1720
    1721        ordinality = expression.args.get("ordinality") or ""
    1722        if ordinality:
    1723            ordinality = f" WITH ORDINALITY{alias}"
    1724            alias = ""
    1725
    1726        when = self.sql(expression, "when")
    1727        if when:
    1728            table = f"{table} {when}"
    1729
    1730        return f"{only}{table}{partition}{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: View Source

    1732    def tablesample_sql(
    1733        self,
    1734        expression: exp.TableSample,
    1735        sep: str = " AS ",
    1736        tablesample_keyword: t.Optional[str] = None,
    1737    ) -> str:
    1738        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1739            table = expression.this.copy()
    1740            table.set("alias", None)
    1741            this = self.sql(table)
    1742            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1743        else:
    1744            this = self.sql(expression, "this")
    1745            alias = ""
    1746
    1747        method = self.sql(expression, "method")
    1748        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1749        numerator = self.sql(expression, "bucket_numerator")
    1750        denominator = self.sql(expression, "bucket_denominator")
    1751        field = self.sql(expression, "bucket_field")
    1752        field = f" ON {field}" if field else ""
    1753        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1754        seed = self.sql(expression, "seed")
    1755        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1756
    1757        size = self.sql(expression, "size")
    1758        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1759            size = f"{size} ROWS"
    1760
    1761        percent = self.sql(expression, "percent")
    1762        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1763            percent = f"{percent} PERCENT"
    1764
    1765        expr = f"{bucket}{percent}{size}"
    1766        if self.TABLESAMPLE_REQUIRES_PARENS:
    1767            expr = f"({expr})"
    1768
    1769        return (
    1770            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
    1771        )

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

    1773    def pivot_sql(self, expression: exp.Pivot) -> str:
    1774        expressions = self.expressions(expression, flat=True)
    1775
    1776        if expression.this:
    1777            this = self.sql(expression, "this")
    1778            if not expressions:
    1779                return f"UNPIVOT {this}"
    1780
    1781            on = f"{self.seg('ON')} {expressions}"
    1782            using = self.expressions(expression, key="using", flat=True)
    1783            using = f"{self.seg('USING')} {using}" if using else ""
    1784            group = self.sql(expression, "group")
    1785            return f"PIVOT {this}{on}{using}{group}"
    1786
    1787        alias = self.sql(expression, "alias")
    1788        alias = f" AS {alias}" if alias else ""
    1789        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    1790        field = self.sql(expression, "field")
    1791        include_nulls = expression.args.get("include_nulls")
    1792        if include_nulls is not None:
    1793            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1794        else:
    1795            nulls = ""
    1796        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1798    def version_sql(self, expression: exp.Version) -> str:
    1799        this = f"FOR {expression.name}"
    1800        kind = expression.text("kind")
    1801        expr = self.sql(expression, "expression")
    1802        return f"{this} {kind} {expr}"

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

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

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

    1807    def update_sql(self, expression: exp.Update) -> str:
    1808        this = self.sql(expression, "this")
    1809        set_sql = self.expressions(expression, flat=True)
    1810        from_sql = self.sql(expression, "from")
    1811        where_sql = self.sql(expression, "where")
    1812        returning = self.sql(expression, "returning")
    1813        order = self.sql(expression, "order")
    1814        limit = self.sql(expression, "limit")
    1815        if self.RETURNING_END:
    1816            expression_sql = f"{from_sql}{where_sql}{returning}"
    1817        else:
    1818            expression_sql = f"{returning}{from_sql}{where_sql}"
    1819        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1820        return self.prepend_ctes(expression, sql)

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

    1822    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    1823        values_as_table = values_as_table and self.VALUES_AS_TABLE
    1824
    1825        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1826        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    1827            args = self.expressions(expression)
    1828            alias = self.sql(expression, "alias")
    1829            values = f"VALUES{self.seg('')}{args}"
    1830            values = (
    1831                f"({values})"
    1832                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1833                else values
    1834            )
    1835            return f"{values} AS {alias}" if alias else values
    1836
    1837        # Converts `VALUES...` expression into a series of select unions.
    1838        alias_node = expression.args.get("alias")
    1839        column_names = alias_node and alias_node.columns
    1840
    1841        selects: t.List[exp.Query] = []
    1842
    1843        for i, tup in enumerate(expression.expressions):
    1844            row = tup.expressions
    1845
    1846            if i == 0 and column_names:
    1847                row = [
    1848                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1849                ]
    1850
    1851            selects.append(exp.Select(expressions=row))
    1852
    1853        if self.pretty:
    1854            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1855            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1856            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1857            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1858            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    1859
    1860        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1861        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1862        return f"({unions}){alias}"

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

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

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

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

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

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

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

    1875    def group_sql(self, expression: exp.Group) -> str:
    1876        group_by_all = expression.args.get("all")
    1877        if group_by_all is True:
    1878            modifier = " ALL"
    1879        elif group_by_all is False:
    1880            modifier = " DISTINCT"
    1881        else:
    1882            modifier = ""
    1883
    1884        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    1885
    1886        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1887        grouping_sets = (
    1888            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1889        )
    1890
    1891        cube = expression.args.get("cube", [])
    1892        if seq_get(cube, 0) is True:
    1893            return f"{group_by}{self.seg('WITH CUBE')}"
    1894        else:
    1895            cube_sql = self.expressions(expression, key="cube", indent=False)
    1896            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1897
    1898        rollup = expression.args.get("rollup", [])
    1899        if seq_get(rollup, 0) is True:
    1900            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1901        else:
    1902            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1903            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1904
    1905        groupings = csv(
    1906            grouping_sets,
    1907            cube_sql,
    1908            rollup_sql,
    1909            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1910            sep=self.GROUPINGS_SEP,
    1911        )
    1912
    1913        if expression.args.get("expressions") and groupings:
    1914            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1915
    1916        return f"{group_by}{groupings}"

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

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

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

    1922    def connect_sql(self, expression: exp.Connect) -> str:
    1923        start = self.sql(expression, "start")
    1924        start = self.seg(f"START WITH {start}") if start else ""
    1925        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    1926        connect = self.sql(expression, "connect")
    1927        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    1928        return start + connect

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

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

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

    1933    def join_sql(self, expression: exp.Join) -> str:
    1934        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1935            side = None
    1936        else:
    1937            side = expression.side
    1938
    1939        op_sql = " ".join(
    1940            op
    1941            for op in (
    1942                expression.method,
    1943                "GLOBAL" if expression.args.get("global") else None,
    1944                side,
    1945                expression.kind,
    1946                expression.hint if self.JOIN_HINTS else None,
    1947            )
    1948            if op
    1949        )
    1950        match_cond = self.sql(expression, "match_condition")
    1951        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    1952        on_sql = self.sql(expression, "on")
    1953        using = expression.args.get("using")
    1954
    1955        if not on_sql and using:
    1956            on_sql = csv(*(self.sql(column) for column in using))
    1957
    1958        this = expression.this
    1959        this_sql = self.sql(this)
    1960
    1961        if on_sql:
    1962            on_sql = self.indent(on_sql, skip_first=True)
    1963            space = self.seg(" " * self.pad) if self.pretty else " "
    1964            if using:
    1965                on_sql = f"{space}USING ({on_sql})"
    1966            else:
    1967                on_sql = f"{space}ON {on_sql}"
    1968        elif not op_sql:
    1969            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    1970                return f" {this_sql}"
    1971
    1972            return f", {this_sql}"
    1973
    1974        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1975        return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"

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

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

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

    1982    def lateral_op(self, expression: exp.Lateral) -> str:
    1983        cross_apply = expression.args.get("cross_apply")
    1984
    1985        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    1986        if cross_apply is True:
    1987            op = "INNER JOIN "
    1988        elif cross_apply is False:
    1989            op = "LEFT JOIN "
    1990        else:
    1991            op = ""
    1992
    1993        return f"{op}LATERAL"

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

    1995    def lateral_sql(self, expression: exp.Lateral) -> str:
    1996        this = self.sql(expression, "this")
    1997
    1998        if expression.args.get("view"):
    1999            alias = expression.args["alias"]
    2000            columns = self.expressions(alias, key="columns", flat=True)
    2001            table = f" {alias.name}" if alias.name else ""
    2002            columns = f" AS {columns}" if columns else ""
    2003            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2004            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2005
    2006        alias = self.sql(expression, "alias")
    2007        alias = f" AS {alias}" if alias else ""
    2008        return f"{self.lateral_op(expression)} {this}{alias}"

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

    2010    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2011        this = self.sql(expression, "this")
    2012
    2013        args = [
    2014            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2015            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2016            if e
    2017        ]
    2018
    2019        args_sql = ", ".join(self.sql(e) for e in args)
    2020        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2021        expressions = self.expressions(expression, flat=True)
    2022        expressions = f" BY {expressions}" if expressions else ""
    2023
    2024        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    2026    def offset_sql(self, expression: exp.Offset) -> str:
    2027        this = self.sql(expression, "this")
    2028        value = expression.expression
    2029        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2030        expressions = self.expressions(expression, flat=True)
    2031        expressions = f" BY {expressions}" if expressions else ""
    2032        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    2034    def setitem_sql(self, expression: exp.SetItem) -> str:
    2035        kind = self.sql(expression, "kind")
    2036        kind = f"{kind} " if kind else ""
    2037        this = self.sql(expression, "this")
    2038        expressions = self.expressions(expression)
    2039        collate = self.sql(expression, "collate")
    2040        collate = f" COLLATE {collate}" if collate else ""
    2041        global_ = "GLOBAL " if expression.args.get("global") else ""
    2042        return f"{global_}{kind}{this}{expressions}{collate}"

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

    2044    def set_sql(self, expression: exp.Set) -> str:
    2045        expressions = (
    2046            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2047        )
    2048        tag = " TAG" if expression.args.get("tag") else ""
    2049        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    2054    def lock_sql(self, expression: exp.Lock) -> str:
    2055        if not self.LOCKING_READS_SUPPORTED:
    2056            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2057            return ""
    2058
    2059        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2060        expressions = self.expressions(expression, flat=True)
    2061        expressions = f" OF {expressions}" if expressions else ""
    2062        wait = expression.args.get("wait")
    2063
    2064        if wait is not None:
    2065            if isinstance(wait, exp.Literal):
    2066                wait = f" WAIT {self.sql(wait)}"
    2067            else:
    2068                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2069
    2070        return f"{lock_type}{expressions}{wait or ''}"

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

    2072    def literal_sql(self, expression: exp.Literal) -> str:
    2073        text = expression.this or ""
    2074        if expression.is_string:
    2075            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2076        return text

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

    2078    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2079        if self.dialect.ESCAPED_SEQUENCES:
    2080            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2081            text = "".join(
    2082                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2083            )
    2084
    2085        return self._replace_line_breaks(text).replace(
    2086            self.dialect.QUOTE_END, self._escaped_quote_end
    2087        )

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

    2089    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2090        local = " LOCAL" if expression.args.get("local") else ""
    2091        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2092        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2093        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2094        partition = self.sql(expression, "partition")
    2095        partition = f" {partition}" if partition else ""
    2096        input_format = self.sql(expression, "input_format")
    2097        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2098        serde = self.sql(expression, "serde")
    2099        serde = f" SERDE {serde}" if serde else ""
    2100        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2102    def null_sql(self, *_) -> str:
    2103        return "NULL"

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

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

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

    2108    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2109        this = self.sql(expression, "this")
    2110        this = f"{this} " if this else this
    2111        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2112        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2113        interpolated_values = [
    2114            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
    2115            for named_expression in expression.args.get("interpolate") or []
    2116        ]
    2117        interpolate = (
    2118            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2119        )
    2120        return f"{order}{interpolate}"

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

    2122    def withfill_sql(self, expression: exp.WithFill) -> str:
    2123        from_sql = self.sql(expression, "from")
    2124        from_sql = f" FROM {from_sql}" if from_sql else ""
    2125        to_sql = self.sql(expression, "to")
    2126        to_sql = f" TO {to_sql}" if to_sql else ""
    2127        step_sql = self.sql(expression, "step")
    2128        step_sql = f" STEP {step_sql}" if step_sql else ""
    2129        return f"WITH FILL{from_sql}{to_sql}{step_sql}"

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

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

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

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

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

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

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

    2140    def ordered_sql(self, expression: exp.Ordered) -> str:
    2141        desc = expression.args.get("desc")
    2142        asc = not desc
    2143
    2144        nulls_first = expression.args.get("nulls_first")
    2145        nulls_last = not nulls_first
    2146        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2147        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2148        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2149
    2150        this = self.sql(expression, "this")
    2151
    2152        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2153        nulls_sort_change = ""
    2154        if nulls_first and (
    2155            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2156        ):
    2157            nulls_sort_change = " NULLS FIRST"
    2158        elif (
    2159            nulls_last
    2160            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2161            and not nulls_are_last
    2162        ):
    2163            nulls_sort_change = " NULLS LAST"
    2164
    2165        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2166        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2167            window = expression.find_ancestor(exp.Window, exp.Select)
    2168            if isinstance(window, exp.Window) and window.args.get("spec"):
    2169                self.unsupported(
    2170                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2171                )
    2172                nulls_sort_change = ""
    2173            elif self.NULL_ORDERING_SUPPORTED is None:
    2174                if expression.this.is_int:
    2175                    self.unsupported(
    2176                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2177                    )
    2178                elif not isinstance(expression.this, exp.Rand):
    2179                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2180                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2181                nulls_sort_change = ""
    2182
    2183        with_fill = self.sql(expression, "with_fill")
    2184        with_fill = f" {with_fill}" if with_fill else ""
    2185
    2186        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

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

    2188    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2189        window_frame = self.sql(expression, "window_frame")
    2190        window_frame = f"{window_frame} " if window_frame else ""
    2191
    2192        this = self.sql(expression, "this")
    2193
    2194        return f"{window_frame}{this}"

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

    2196    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2197        partition = self.partition_by_sql(expression)
    2198        order = self.sql(expression, "order")
    2199        measures = self.expressions(expression, key="measures")
    2200        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2201        rows = self.sql(expression, "rows")
    2202        rows = self.seg(rows) if rows else ""
    2203        after = self.sql(expression, "after")
    2204        after = self.seg(after) if after else ""
    2205        pattern = self.sql(expression, "pattern")
    2206        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2207        definition_sqls = [
    2208            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2209            for definition in expression.args.get("define", [])
    2210        ]
    2211        definitions = self.expressions(sqls=definition_sqls)
    2212        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2213        body = "".join(
    2214            (
    2215                partition,
    2216                order,
    2217                measures,
    2218                rows,
    2219                after,
    2220                pattern,
    2221                define,
    2222            )
    2223        )
    2224        alias = self.sql(expression, "alias")
    2225        alias = f" {alias}" if alias else ""
    2226        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2228    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2229        limit = expression.args.get("limit")
    2230
    2231        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2232            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2233        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2234            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2235
    2236        options = self.expressions(expression, key="options")
    2237        if options:
    2238            options = f" OPTION{self.wrap(options)}"
    2239
    2240        return csv(
    2241            *sqls,
    2242            *[self.sql(join) for join in expression.args.get("joins") or []],
    2243            self.sql(expression, "connect"),
    2244            self.sql(expression, "match"),
    2245            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2246            self.sql(expression, "prewhere"),
    2247            self.sql(expression, "where"),
    2248            self.sql(expression, "group"),
    2249            self.sql(expression, "having"),
    2250            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2251            self.sql(expression, "order"),
    2252            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2253            *self.after_limit_modifiers(expression),
    2254            options,
    2255            sep="",
    2256        )

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

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

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

    2261    def offset_limit_modifiers(
    2262        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2263    ) -> t.List[str]:
    2264        return [
    2265            self.sql(expression, "offset") if fetch else self.sql(limit),
    2266            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2267        ]

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

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

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

    2274    def select_sql(self, expression: exp.Select) -> str:
    2275        into = expression.args.get("into")
    2276        if not self.SUPPORTS_SELECT_INTO and into:
    2277            into.pop()
    2278
    2279        hint = self.sql(expression, "hint")
    2280        distinct = self.sql(expression, "distinct")
    2281        distinct = f" {distinct}" if distinct else ""
    2282        kind = self.sql(expression, "kind")
    2283
    2284        limit = expression.args.get("limit")
    2285        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2286            top = self.limit_sql(limit, top=True)
    2287            limit.pop()
    2288        else:
    2289            top = ""
    2290
    2291        expressions = self.expressions(expression)
    2292
    2293        if kind:
    2294            if kind in self.SELECT_KINDS:
    2295                kind = f" AS {kind}"
    2296            else:
    2297                if kind == "STRUCT":
    2298                    expressions = self.expressions(
    2299                        sqls=[
    2300                            self.sql(
    2301                                exp.Struct(
    2302                                    expressions=[
    2303                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2304                                        if isinstance(e, exp.Alias)
    2305                                        else e
    2306                                        for e in expression.expressions
    2307                                    ]
    2308                                )
    2309                            )
    2310                        ]
    2311                    )
    2312                kind = ""
    2313
    2314        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2315        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2316        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2317        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2318        sql = self.query_modifiers(
    2319            expression,
    2320            f"SELECT{top_distinct}{kind}{expressions}",
    2321            self.sql(expression, "into", comment=False),
    2322            self.sql(expression, "from", comment=False),
    2323        )
    2324
    2325        sql = self.prepend_ctes(expression, sql)
    2326
    2327        if not self.SUPPORTS_SELECT_INTO and into:
    2328            if into.args.get("temporary"):
    2329                table_kind = " TEMPORARY"
    2330            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2331                table_kind = " UNLOGGED"
    2332            else:
    2333                table_kind = ""
    2334            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2335
    2336        return sql

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

    2338    def schema_sql(self, expression: exp.Schema) -> str:
    2339        this = self.sql(expression, "this")
    2340        sql = self.schema_columns_sql(expression)
    2341        return f"{this} {sql}" if this and sql else this or sql

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

    2343    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2344        if expression.expressions:
    2345            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2346        return ""

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

    2348    def star_sql(self, expression: exp.Star) -> str:
    2349        except_ = self.expressions(expression, key="except", flat=True)
    2350        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2351        replace = self.expressions(expression, key="replace", flat=True)
    2352        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2353        rename = self.expressions(expression, key="rename", flat=True)
    2354        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2355        return f"*{except_}{replace}{rename}"

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

    2357    def parameter_sql(self, expression: exp.Parameter) -> str:
    2358        this = self.sql(expression, "this")
    2359        return f"{self.PARAMETER_TOKEN}{this}"

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

    2361    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2362        this = self.sql(expression, "this")
    2363        kind = expression.text("kind")
    2364        if kind:
    2365            kind = f"{kind}."
    2366        return f"@@{kind}{this}"

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

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

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

    2371    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2372        alias = self.sql(expression, "alias")
    2373        alias = f"{sep}{alias}" if alias else ""
    2374
    2375        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2376        pivots = f" {pivots}" if pivots else ""
    2377
    2378        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2379        return self.prepend_ctes(expression, sql)

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

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

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

    2385    def set_operations(self, expression: exp.Union) -> str:
    2386        if not self.OUTER_UNION_MODIFIERS:
    2387            limit = expression.args.get("limit")
    2388            order = expression.args.get("order")
    2389
    2390            if limit or order:
    2391                select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    2392
    2393                if limit:
    2394                    select = select.limit(limit.pop(), copy=False)
    2395                if order:
    2396                    select = select.order_by(order.pop(), copy=False)
    2397                return self.sql(select)
    2398
    2399        sqls: t.List[str] = []
    2400        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    2401
    2402        while stack:
    2403            node = stack.pop()
    2404
    2405            if isinstance(node, exp.Union):
    2406                stack.append(node.expression)
    2407                stack.append(
    2408                    self.maybe_comment(
    2409                        getattr(self, f"{node.key}_op")(node),
    2410                        comments=node.comments,
    2411                        separated=True,
    2412                    )
    2413                )
    2414                stack.append(node.this)
    2415            else:
    2416                sqls.append(self.sql(node))
    2417
    2418        this = self.sep().join(sqls)
    2419        this = self.query_modifiers(expression, this)
    2420        return self.prepend_ctes(expression, this)

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

    2422    def union_sql(self, expression: exp.Union) -> str:
    2423        return self.set_operations(expression)

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

    2425    def union_op(self, expression: exp.Union) -> str:
    2426        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2427        kind = kind if expression.args.get("distinct") else " ALL"
    2428        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2429        return f"UNION{kind}{by_name}"

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

    2431    def unnest_sql(self, expression: exp.Unnest) -> str:
    2432        args = self.expressions(expression, flat=True)
    2433
    2434        alias = expression.args.get("alias")
    2435        offset = expression.args.get("offset")
    2436
    2437        if self.UNNEST_WITH_ORDINALITY:
    2438            if alias and isinstance(offset, exp.Expression):
    2439                alias.append("columns", offset)
    2440
    2441        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2442            columns = alias.columns
    2443            alias = self.sql(columns[0]) if columns else ""
    2444        else:
    2445            alias = self.sql(alias)
    2446
    2447        alias = f" AS {alias}" if alias else alias
    2448        if self.UNNEST_WITH_ORDINALITY:
    2449            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2450        else:
    2451            if isinstance(offset, exp.Expression):
    2452                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2453            elif offset:
    2454                suffix = f"{alias} WITH OFFSET"
    2455            else:
    2456                suffix = alias
    2457
    2458        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2467    def window_sql(self, expression: exp.Window) -> str:
    2468        this = self.sql(expression, "this")
    2469        partition = self.partition_by_sql(expression)
    2470        order = expression.args.get("order")
    2471        order = self.order_sql(order, flat=True) if order else ""
    2472        spec = self.sql(expression, "spec")
    2473        alias = self.sql(expression, "alias")
    2474        over = self.sql(expression, "over") or "OVER"
    2475
    2476        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2477
    2478        first = expression.args.get("first")
    2479        if first is None:
    2480            first = ""
    2481        else:
    2482            first = "FIRST" if first else "LAST"
    2483
    2484        if not partition and not order and not spec and alias:
    2485            return f"{this} {alias}"
    2486
    2487        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2488        return f"{this} ({args})"

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

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

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

    2494    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2495        kind = self.sql(expression, "kind")
    2496        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2497        end = (
    2498            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2499            or "CURRENT ROW"
    2500        )
    2501        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2508    def between_sql(self, expression: exp.Between) -> str:
    2509        this = self.sql(expression, "this")
    2510        low = self.sql(expression, "low")
    2511        high = self.sql(expression, "high")
    2512        return f"{this} BETWEEN {low} AND {high}"

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

    2514    def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]:
    2515        return apply_index_offset(
    2516            expression.this,
    2517            expression.expressions,
    2518            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2519        )

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

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

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

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

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

    2529    def any_sql(self, expression: exp.Any) -> str:
    2530        this = self.sql(expression, "this")
    2531        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2532            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2533                this = self.wrap(this)
    2534            return f"ANY{this}"
    2535        return f"ANY {this}"

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

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

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

    2540    def case_sql(self, expression: exp.Case) -> str:
    2541        this = self.sql(expression, "this")
    2542        statements = [f"CASE {this}" if this else "CASE"]
    2543
    2544        for e in expression.args["ifs"]:
    2545            statements.append(f"WHEN {self.sql(e, 'this')}")
    2546            statements.append(f"THEN {self.sql(e, 'true')}")
    2547
    2548        default = self.sql(expression, "default")
    2549
    2550        if default:
    2551            statements.append(f"ELSE {default}")
    2552
    2553        statements.append("END")
    2554
    2555        if self.pretty and self.too_wide(statements):
    2556            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2557
    2558        return " ".join(statements)

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

    2560    def constraint_sql(self, expression: exp.Constraint) -> str:
    2561        this = self.sql(expression, "this")
    2562        expressions = self.expressions(expression, flat=True)
    2563        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2570    def extract_sql(self, expression: exp.Extract) -> str:
    2571        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2572        expression_sql = self.sql(expression, "expression")
    2573        return f"EXTRACT({this} FROM {expression_sql})"

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

    2575    def trim_sql(self, expression: exp.Trim) -> str:
    2576        trim_type = self.sql(expression, "position")
    2577
    2578        if trim_type == "LEADING":
    2579            return self.func("LTRIM", expression.this)
    2580        elif trim_type == "TRAILING":
    2581            return self.func("RTRIM", expression.this)
    2582        else:
    2583            return self.func("TRIM", expression.this, expression.expression)

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

    2585    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2586        args = expression.expressions
    2587        if isinstance(expression, exp.ConcatWs):
    2588            args = args[1:]  # Skip the delimiter
    2589
    2590        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2591            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2592
    2593        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2594            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2595
    2596        return args

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

    2598    def concat_sql(self, expression: exp.Concat) -> str:
    2599        expressions = self.convert_concat_args(expression)
    2600
    2601        # Some dialects don't allow a single-argument CONCAT call
    2602        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2603            return self.sql(expressions[0])
    2604
    2605        return self.func("CONCAT", *expressions)

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

    2607    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2608        return self.func(
    2609            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2610        )

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

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

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

    2616    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2617        expressions = self.expressions(expression, flat=True)
    2618        reference = self.sql(expression, "reference")
    2619        reference = f" {reference}" if reference else ""
    2620        delete = self.sql(expression, "delete")
    2621        delete = f" ON DELETE {delete}" if delete else ""
    2622        update = self.sql(expression, "update")
    2623        update = f" ON UPDATE {update}" if update else ""
    2624        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2626    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2627        expressions = self.expressions(expression, flat=True)
    2628        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2629        options = f" {options}" if options else ""
    2630        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

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

    2643    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2644        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2645        return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"

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

    2647    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2648        if isinstance(expression, exp.JSONPathPart):
    2649            transform = self.TRANSFORMS.get(expression.__class__)
    2650            if not callable(transform):
    2651                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2652                return ""
    2653
    2654            return transform(self, expression)
    2655
    2656        if isinstance(expression, int):
    2657            return str(expression)
    2658
    2659        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2660            escaped = expression.replace("'", "\\'")
    2661            escaped = f"\\'{expression}\\'"
    2662        else:
    2663            escaped = expression.replace('"', '\\"')
    2664            escaped = f'"{escaped}"'
    2665
    2666        return escaped

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

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

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

    2671    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2672        null_handling = expression.args.get("null_handling")
    2673        null_handling = f" {null_handling}" if null_handling else ""
    2674
    2675        unique_keys = expression.args.get("unique_keys")
    2676        if unique_keys is not None:
    2677            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2678        else:
    2679            unique_keys = ""
    2680
    2681        return_type = self.sql(expression, "return_type")
    2682        return_type = f" RETURNING {return_type}" if return_type else ""
    2683        encoding = self.sql(expression, "encoding")
    2684        encoding = f" ENCODING {encoding}" if encoding else ""
    2685
    2686        return self.func(
    2687            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2688            *expression.expressions,
    2689            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2690        )

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

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

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

    2695    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2696        null_handling = expression.args.get("null_handling")
    2697        null_handling = f" {null_handling}" if null_handling else ""
    2698        return_type = self.sql(expression, "return_type")
    2699        return_type = f" RETURNING {return_type}" if return_type else ""
    2700        strict = " STRICT" if expression.args.get("strict") else ""
    2701        return self.func(
    2702            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2703        )

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

    2705    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2706        this = self.sql(expression, "this")
    2707        order = self.sql(expression, "order")
    2708        null_handling = expression.args.get("null_handling")
    2709        null_handling = f" {null_handling}" if null_handling else ""
    2710        return_type = self.sql(expression, "return_type")
    2711        return_type = f" RETURNING {return_type}" if return_type else ""
    2712        strict = " STRICT" if expression.args.get("strict") else ""
    2713        return self.func(
    2714            "JSON_ARRAYAGG",
    2715            this,
    2716            suffix=f"{order}{null_handling}{return_type}{strict})",
    2717        )

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

    2719    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2720        path = self.sql(expression, "path")
    2721        path = f" PATH {path}" if path else ""
    2722        nested_schema = self.sql(expression, "nested_schema")
    2723
    2724        if nested_schema:
    2725            return f"NESTED{path} {nested_schema}"
    2726
    2727        this = self.sql(expression, "this")
    2728        kind = self.sql(expression, "kind")
    2729        kind = f" {kind}" if kind else ""
    2730        return f"{this}{kind}{path}"

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

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

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

    2735    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2736        this = self.sql(expression, "this")
    2737        path = self.sql(expression, "path")
    2738        path = f", {path}" if path else ""
    2739        error_handling = expression.args.get("error_handling")
    2740        error_handling = f" {error_handling}" if error_handling else ""
    2741        empty_handling = expression.args.get("empty_handling")
    2742        empty_handling = f" {empty_handling}" if empty_handling else ""
    2743        schema = self.sql(expression, "schema")
    2744        return self.func(
    2745            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2746        )

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

    2748    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2749        this = self.sql(expression, "this")
    2750        kind = self.sql(expression, "kind")
    2751        path = self.sql(expression, "path")
    2752        path = f" {path}" if path else ""
    2753        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2754        return f"{this} {kind}{path}{as_json}"

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

    2756    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2757        this = self.sql(expression, "this")
    2758        path = self.sql(expression, "path")
    2759        path = f", {path}" if path else ""
    2760        expressions = self.expressions(expression)
    2761        with_ = (
    2762            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2763            if expressions
    2764            else ""
    2765        )
    2766        return f"OPENJSON({this}{path}){with_}"

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

    2768    def in_sql(self, expression: exp.In) -> str:
    2769        query = expression.args.get("query")
    2770        unnest = expression.args.get("unnest")
    2771        field = expression.args.get("field")
    2772        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2773
    2774        if query:
    2775            in_sql = self.sql(query)
    2776        elif unnest:
    2777            in_sql = self.in_unnest_op(unnest)
    2778        elif field:
    2779            in_sql = self.sql(field)
    2780        else:
    2781            in_sql = f"({self.expressions(expression, flat=True)})"
    2782
    2783        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2788    def interval_sql(self, expression: exp.Interval) -> str:
    2789        unit = self.sql(expression, "unit")
    2790        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2791            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    2792        unit = f" {unit}" if unit else ""
    2793
    2794        if self.SINGLE_STRING_INTERVAL:
    2795            this = expression.this.name if expression.this else ""
    2796            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2797
    2798        this = self.sql(expression, "this")
    2799        if this:
    2800            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2801            this = f" {this}" if unwrapped else f" ({this})"
    2802
    2803        return f"INTERVAL{this}{unit}"

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

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

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

    2808    def reference_sql(self, expression: exp.Reference) -> str:
    2809        this = self.sql(expression, "this")
    2810        expressions = self.expressions(expression, flat=True)
    2811        expressions = f"({expressions})" if expressions else ""
    2812        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2813        options = f" {options}" if options else ""
    2814        return f"REFERENCES {this}{expressions}{options}"

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

    2816    def anonymous_sql(self, expression: exp.Anonymous) -> str:
    2817        return self.func(self.sql(expression, "this"), *expression.expressions)

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

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

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

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

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

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

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

    2832    def alias_sql(self, expression: exp.Alias) -> str:
    2833        alias = self.sql(expression, "alias")
    2834        alias = f" AS {alias}" if alias else ""
    2835        return f"{self.sql(expression, 'this')}{alias}"

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

    2837    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    2838        alias = expression.args["alias"]
    2839        identifier_alias = isinstance(alias, exp.Identifier)
    2840
    2841        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2842            alias.replace(exp.Literal.string(alias.output_name))
    2843        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2844            alias.replace(exp.to_identifier(alias.output_name))
    2845
    2846        return self.alias_sql(expression)

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

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

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

    2851    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    2852        this = self.sql(expression, "this")
    2853        index = self.sql(expression, "expression")
    2854        return f"{this} AT {index}"

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

    2856    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2857        this = self.sql(expression, "this")
    2858        zone = self.sql(expression, "zone")
    2859        return f"{this} AT TIME ZONE {zone}"

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

    2861    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    2862        this = self.sql(expression, "this")
    2863        zone = self.sql(expression, "zone")
    2864        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

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

    2869    def and_sql(
    2870        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    2871    ) -> str:
    2872        return self.connector_sql(expression, "AND", stack)

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

    2874    def or_sql(
    2875        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    2876    ) -> str:
    2877        return self.connector_sql(expression, "OR", stack)

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

    2879    def xor_sql(
    2880        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    2881    ) -> str:
    2882        return self.connector_sql(expression, "XOR", stack)

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

    2884    def connector_sql(
    2885        self,
    2886        expression: exp.Connector,
    2887        op: str,
    2888        stack: t.Optional[t.List[str | exp.Expression]] = None,
    2889    ) -> str:
    2890        if stack is not None:
    2891            if expression.expressions:
    2892                stack.append(self.expressions(expression, sep=f" {op} "))
    2893            else:
    2894                stack.append(expression.right)
    2895                if expression.comments and self.comments:
    2896                    for comment in expression.comments:
    2897                        if comment:
    2898                            op += f" /*{self.pad_comment(comment)}*/"
    2899                stack.extend((op, expression.left))
    2900            return op
    2901
    2902        stack = [expression]
    2903        sqls: t.List[str] = []
    2904        ops = set()
    2905
    2906        while stack:
    2907            node = stack.pop()
    2908            if isinstance(node, exp.Connector):
    2909                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    2910            else:
    2911                sql = self.sql(node)
    2912                if sqls and sqls[-1] in ops:
    2913                    sqls[-1] += f" {sql}"
    2914                else:
    2915                    sqls.append(sql)
    2916
    2917        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    2918        return sep.join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2938    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2939        format_sql = self.sql(expression, "format")
    2940        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2941        to_sql = self.sql(expression, "to")
    2942        to_sql = f" {to_sql}" if to_sql else ""
    2943        action = self.sql(expression, "action")
    2944        action = f" {action}" if action else ""
    2945        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})"

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

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

def currenttimestamp_sql(self, expression: sqlglot.expressions.CurrentTimestamp) -> str: View Source

    2951    def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str:
    2952        return self.func("CURRENT_TIMESTAMP", expression.this)

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

    2954    def collate_sql(self, expression: exp.Collate) -> str:
    2955        if self.COLLATE_IS_FUNC:
    2956            return self.function_fallback_sql(expression)
    2957        return self.binary(expression, "COLLATE")

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

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

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

    2962    def comment_sql(self, expression: exp.Comment) -> str:
    2963        this = self.sql(expression, "this")
    2964        kind = expression.args["kind"]
    2965        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    2966        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2967        expression_sql = self.sql(expression, "expression")
    2968        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

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

    2970    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2971        this = self.sql(expression, "this")
    2972        delete = " DELETE" if expression.args.get("delete") else ""
    2973        recompress = self.sql(expression, "recompress")
    2974        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2975        to_disk = self.sql(expression, "to_disk")
    2976        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2977        to_volume = self.sql(expression, "to_volume")
    2978        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2979        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2981    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2982        where = self.sql(expression, "where")
    2983        group = self.sql(expression, "group")
    2984        aggregates = self.expressions(expression, key="aggregates")
    2985        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2986
    2987        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2988            return f"TTL {self.expressions(expression, flat=True)}"
    2989
    2990        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2995    def commit_sql(self, expression: exp.Commit) -> str:
    2996        chain = expression.args.get("chain")
    2997        if chain is not None:
    2998            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2999
    3000        return f"COMMIT{chain or ''}"

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

    3002    def rollback_sql(self, expression: exp.Rollback) -> str:
    3003        savepoint = expression.args.get("savepoint")
    3004        savepoint = f" TO {savepoint}" if savepoint else ""
    3005        return f"ROLLBACK{savepoint}"

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

    3007    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3008        this = self.sql(expression, "this")
    3009
    3010        dtype = self.sql(expression, "dtype")
    3011        if dtype:
    3012            collate = self.sql(expression, "collate")
    3013            collate = f" COLLATE {collate}" if collate else ""
    3014            using = self.sql(expression, "using")
    3015            using = f" USING {using}" if using else ""
    3016            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    3017
    3018        default = self.sql(expression, "default")
    3019        if default:
    3020            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3021
    3022        comment = self.sql(expression, "comment")
    3023        if comment:
    3024            return f"ALTER COLUMN {this} COMMENT {comment}"
    3025
    3026        if not expression.args.get("drop"):
    3027            self.unsupported("Unsupported ALTER COLUMN syntax")
    3028
    3029        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    3031    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3032        this = self.sql(expression, "this")
    3033        if not isinstance(expression.this, exp.Var):
    3034            this = f"KEY DISTKEY {this}"
    3035        return f"ALTER DISTSTYLE {this}"

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

    3037    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3038        compound = " COMPOUND" if expression.args.get("compound") else ""
    3039        this = self.sql(expression, "this")
    3040        expressions = self.expressions(expression, flat=True)
    3041        expressions = f"({expressions})" if expressions else ""
    3042        return f"ALTER{compound} SORTKEY {this or expressions}"

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

    3044    def renametable_sql(self, expression: exp.RenameTable) -> str:
    3045        if not self.RENAME_TABLE_WITH_DB:
    3046            # Remove db from tables
    3047            expression = expression.transform(
    3048                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3049            ).assert_is(exp.RenameTable)
    3050        this = self.sql(expression, "this")
    3051        return f"RENAME TO {this}"

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

    3053    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3054        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3055        old_column = self.sql(expression, "this")
    3056        new_column = self.sql(expression, "to")
    3057        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

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

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

    3063    def altertable_sql(self, expression: exp.AlterTable) -> str:
    3064        actions = expression.args["actions"]
    3065
    3066        if isinstance(actions[0], exp.ColumnDef):
    3067            actions = self.add_column_sql(expression)
    3068        elif isinstance(actions[0], exp.Schema):
    3069            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3070        elif isinstance(actions[0], exp.Delete):
    3071            actions = self.expressions(expression, key="actions", flat=True)
    3072        else:
    3073            actions = self.expressions(expression, key="actions", flat=True)
    3074
    3075        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3076        on_cluster = self.sql(expression, "cluster")
    3077        on_cluster = f" {on_cluster}" if on_cluster else ""
    3078        only = " ONLY" if expression.args.get("only") else ""
    3079        options = self.expressions(expression, key="options")
    3080        options = f", {options}" if options else ""
    3081        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"

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

    3083    def add_column_sql(self, expression: exp.AlterTable) -> str:
    3084        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3085            return self.expressions(
    3086                expression,
    3087                key="actions",
    3088                prefix="ADD COLUMN ",
    3089            )
    3090        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    3092    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3093        expressions = self.expressions(expression)
    3094        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3095        return f"DROP{exists}{expressions}"

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

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

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

    3100    def distinct_sql(self, expression: exp.Distinct) -> str:
    3101        this = self.expressions(expression, flat=True)
    3102
    3103        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3104            case = exp.case()
    3105            for arg in expression.expressions:
    3106                case = case.when(arg.is_(exp.null()), exp.null())
    3107            this = self.sql(case.else_(f"({this})"))
    3108
    3109        this = f" {this}" if this else ""
    3110
    3111        on = self.sql(expression, "on")
    3112        on = f" ON {on}" if on else ""
    3113        return f"DISTINCT{this}{on}"

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

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

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

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

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

    3121    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3122        this_sql = self.sql(expression, "this")
    3123        expression_sql = self.sql(expression, "expression")
    3124        kind = "MAX" if expression.args.get("max") else "MIN"
    3125        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3127    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3128        return self.sql(
    3129            exp.Cast(
    3130                this=exp.Div(this=expression.this, expression=expression.expression),
    3131                to=exp.DataType(this=exp.DataType.Type.INT),
    3132            )
    3133        )

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

    3135    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3136        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3137            return self.func(
    3138                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3139            )
    3140        return self.binary(expression, "||")

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

    3142    def div_sql(self, expression: exp.Div) -> str:
    3143        l, r = expression.left, expression.right
    3144
    3145        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3146            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3147
    3148        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3149            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3150                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3151
    3152        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3153            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3154                return self.sql(
    3155                    exp.cast(
    3156                        l / r,
    3157                        to=exp.DataType.Type.BIGINT,
    3158                    )
    3159                )
    3160
    3161        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3196    def is_sql(self, expression: exp.Is) -> str:
    3197        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3198            return self.sql(
    3199                expression.this if expression.expression.this else exp.not_(expression.this)
    3200            )
    3201        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3242    def try_sql(self, expression: exp.Try) -> str:
    3243        if not self.TRY_SUPPORTED:
    3244            self.unsupported("Unsupported TRY function")
    3245            return self.sql(expression, "this")
    3246
    3247        return self.func("TRY", expression.this)

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

    3249    def log_sql(self, expression: exp.Log) -> str:
    3250        this = expression.this
    3251        expr = expression.expression
    3252
    3253        if self.dialect.LOG_BASE_FIRST is False:
    3254            this, expr = expr, this
    3255        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3256            if this.name in ("2", "10"):
    3257                return self.func(f"LOG{this.name}", expr)
    3258
    3259            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3260
    3261        return self.func("LOG", this, expr)

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

    3263    def use_sql(self, expression: exp.Use) -> str:
    3264        kind = self.sql(expression, "kind")
    3265        kind = f" {kind}" if kind else ""
    3266        this = self.sql(expression, "this")
    3267        this = f" {this}" if this else ""
    3268        return f"USE{kind}{this}"

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

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

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

    3274    def function_fallback_sql(self, expression: exp.Func) -> str:
    3275        args = []
    3276
    3277        for key in expression.arg_types:
    3278            arg_value = expression.args.get(key)
    3279
    3280            if isinstance(arg_value, list):
    3281                for value in arg_value:
    3282                    args.append(value)
    3283            elif arg_value is not None:
    3284                args.append(arg_value)
    3285
    3286        if self.normalize_functions:
    3287            name = expression.sql_name()
    3288        else:
    3289            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3290
    3291        return self.func(name, *args)

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

    3293    def func(
    3294        self,
    3295        name: str,
    3296        *args: t.Optional[exp.Expression | str],
    3297        prefix: str = "(",
    3298        suffix: str = ")",
    3299    ) -> str:
    3300        return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}"

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

    3302    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    3303        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    3304        if self.pretty and self.too_wide(arg_sqls):
    3305            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    3306        return ", ".join(arg_sqls)

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

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

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

    3311    def format_time(
    3312        self,
    3313        expression: exp.Expression,
    3314        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3315        inverse_time_trie: t.Optional[t.Dict] = None,
    3316    ) -> t.Optional[str]:
    3317        return format_time(
    3318            self.sql(expression, "format"),
    3319            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3320            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3321        )

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

    3323    def expressions(
    3324        self,
    3325        expression: t.Optional[exp.Expression] = None,
    3326        key: t.Optional[str] = None,
    3327        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3328        flat: bool = False,
    3329        indent: bool = True,
    3330        skip_first: bool = False,
    3331        skip_last: bool = False,
    3332        sep: str = ", ",
    3333        prefix: str = "",
    3334        dynamic: bool = False,
    3335        new_line: bool = False,
    3336    ) -> str:
    3337        expressions = expression.args.get(key or "expressions") if expression else sqls
    3338
    3339        if not expressions:
    3340            return ""
    3341
    3342        if flat:
    3343            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3344
    3345        num_sqls = len(expressions)
    3346
    3347        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    3348        if self.pretty and not self.leading_comma:
    3349            stripped_sep = sep.strip()
    3350
    3351        result_sqls = []
    3352        for i, e in enumerate(expressions):
    3353            sql = self.sql(e, comment=False)
    3354            if not sql:
    3355                continue
    3356
    3357            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3358
    3359            if self.pretty:
    3360                if self.leading_comma:
    3361                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3362                else:
    3363                    result_sqls.append(
    3364                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    3365                    )
    3366            else:
    3367                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3368
    3369        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3370            if new_line:
    3371                result_sqls.insert(0, "")
    3372                result_sqls.append("")
    3373            result_sql = "\n".join(result_sqls)
    3374        else:
    3375            result_sql = "".join(result_sqls)
    3376        return (
    3377            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3378            if indent
    3379            else result_sql
    3380        )

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

    3382    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3383        flat = flat or isinstance(expression.parent, exp.Properties)
    3384        expressions_sql = self.expressions(expression, flat=flat)
    3385        if flat:
    3386            return f"{op} {expressions_sql}"
    3387        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3389    def naked_property(self, expression: exp.Property) -> str:
    3390        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3391        if not property_name:
    3392            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3393        return f"{property_name} {self.sql(expression, 'this')}"

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

    3395    def tag_sql(self, expression: exp.Tag) -> str:
    3396        return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}"

def token_sql(self, token_type: sqlglot.tokens.TokenType) -> str: View Source

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

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

    3401    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3402        this = self.sql(expression, "this")
    3403        expressions = self.no_identify(self.expressions, expression)
    3404        expressions = (
    3405            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3406        )
    3407        return f"{this}{expressions}"

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

    3409    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3410        this = self.sql(expression, "this")
    3411        expressions = self.expressions(expression, flat=True)
    3412        return f"{this}({expressions})"

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

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

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

    3417    def when_sql(self, expression: exp.When) -> str:
    3418        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3419        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3420        condition = self.sql(expression, "condition")
    3421        condition = f" AND {condition}" if condition else ""
    3422
    3423        then_expression = expression.args.get("then")
    3424        if isinstance(then_expression, exp.Insert):
    3425            this = self.sql(then_expression, "this")
    3426            this = f"INSERT {this}" if this else "INSERT"
    3427            then = self.sql(then_expression, "expression")
    3428            then = f"{this} VALUES {then}" if then else this
    3429        elif isinstance(then_expression, exp.Update):
    3430            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3431                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3432            else:
    3433                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3434        else:
    3435            then = self.sql(then_expression)
    3436        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3438    def merge_sql(self, expression: exp.Merge) -> str:
    3439        table = expression.this
    3440        table_alias = ""
    3441
    3442        hints = table.args.get("hints")
    3443        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3444            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3445            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3446
    3447        this = self.sql(table)
    3448        using = f"USING {self.sql(expression, 'using')}"
    3449        on = f"ON {self.sql(expression, 'on')}"
    3450        expressions = self.expressions(expression, sep=" ", indent=False)
    3451        sep = self.sep()
    3452
    3453        return self.prepend_ctes(
    3454            expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}"
    3455        )

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

    3457    def tochar_sql(self, expression: exp.ToChar) -> str:
    3458        if expression.args.get("format"):
    3459            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3460
    3461        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

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

    3463    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3464        if not self.SUPPORTS_TO_NUMBER:
    3465            self.unsupported("Unsupported TO_NUMBER function")
    3466            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3467
    3468        fmt = expression.args.get("format")
    3469        if not fmt:
    3470            self.unsupported("Conversion format is required for TO_NUMBER")
    3471            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3472
    3473        return self.func("TO_NUMBER", expression.this, fmt)

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

    3475    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3476        this = self.sql(expression, "this")
    3477        kind = self.sql(expression, "kind")
    3478        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3479        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3480        return f"{this}({kind}{args})"

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

    3482    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3483        this = self.sql(expression, "this")
    3484        max = self.sql(expression, "max")
    3485        min = self.sql(expression, "min")
    3486        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3494    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3495        expressions = self.expressions(expression, key="expressions", flat=True)
    3496        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3497        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3498        buckets = self.sql(expression, "buckets")
    3499        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3501    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3502        this = self.sql(expression, "this")
    3503        having = self.sql(expression, "having")
    3504
    3505        if having:
    3506            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3507
    3508        return self.func("ANY_VALUE", this)

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

    3510    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3511        transform = self.func("TRANSFORM", *expression.expressions)
    3512        row_format_before = self.sql(expression, "row_format_before")
    3513        row_format_before = f" {row_format_before}" if row_format_before else ""
    3514        record_writer = self.sql(expression, "record_writer")
    3515        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3516        using = f" USING {self.sql(expression, 'command_script')}"
    3517        schema = self.sql(expression, "schema")
    3518        schema = f" AS {schema}" if schema else ""
    3519        row_format_after = self.sql(expression, "row_format_after")
    3520        row_format_after = f" {row_format_after}" if row_format_after else ""
    3521        record_reader = self.sql(expression, "record_reader")
    3522        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3523        return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"

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

    3525    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3526        key_block_size = self.sql(expression, "key_block_size")
    3527        if key_block_size:
    3528            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3529
    3530        using = self.sql(expression, "using")
    3531        if using:
    3532            return f"USING {using}"
    3533
    3534        parser = self.sql(expression, "parser")
    3535        if parser:
    3536            return f"WITH PARSER {parser}"
    3537
    3538        comment = self.sql(expression, "comment")
    3539        if comment:
    3540            return f"COMMENT {comment}"
    3541
    3542        visible = expression.args.get("visible")
    3543        if visible is not None:
    3544            return "VISIBLE" if visible else "INVISIBLE"
    3545
    3546        engine_attr = self.sql(expression, "engine_attr")
    3547        if engine_attr:
    3548            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3549
    3550        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3551        if secondary_engine_attr:
    3552            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3553
    3554        self.unsupported("Unsupported index constraint option.")
    3555        return ""

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

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

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

    3561    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3562        kind = self.sql(expression, "kind")
    3563        kind = f"{kind} INDEX" if kind else "INDEX"
    3564        this = self.sql(expression, "this")
    3565        this = f" {this}" if this else ""
    3566        index_type = self.sql(expression, "index_type")
    3567        index_type = f" USING {index_type}" if index_type else ""
    3568        expressions = self.expressions(expression, flat=True)
    3569        expressions = f" ({expressions})" if expressions else ""
    3570        options = self.expressions(expression, key="options", sep=" ")
    3571        options = f" {options}" if options else ""
    3572        return f"{kind}{this}{index_type}{expressions}{options}"

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

    3574    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3575        if self.NVL2_SUPPORTED:
    3576            return self.function_fallback_sql(expression)
    3577
    3578        case = exp.Case().when(
    3579            expression.this.is_(exp.null()).not_(copy=False),
    3580            expression.args["true"],
    3581            copy=False,
    3582        )
    3583        else_cond = expression.args.get("false")
    3584        if else_cond:
    3585            case.else_(else_cond, copy=False)
    3586
    3587        return self.sql(case)

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

    3589    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3590        this = self.sql(expression, "this")
    3591        expr = self.sql(expression, "expression")
    3592        iterator = self.sql(expression, "iterator")
    3593        condition = self.sql(expression, "condition")
    3594        condition = f" IF {condition}" if condition else ""
    3595        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3603    def predict_sql(self, expression: exp.Predict) -> str:
    3604        model = self.sql(expression, "this")
    3605        model = f"MODEL {model}"
    3606        table = self.sql(expression, "expression")
    3607        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3608        parameters = self.sql(expression, "params_struct")
    3609        return self.func("PREDICT", model, table, parameters or None)

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

    3611    def forin_sql(self, expression: exp.ForIn) -> str:
    3612        this = self.sql(expression, "this")
    3613        expression_sql = self.sql(expression, "expression")
    3614        return f"FOR {this} DO {expression_sql}"

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

    3616    def refresh_sql(self, expression: exp.Refresh) -> str:
    3617        this = self.sql(expression, "this")
    3618        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3619        return f"REFRESH {table}{this}"

def operator_sql(self, expression: sqlglot.expressions.Operator) -> str: View Source

    3621    def operator_sql(self, expression: exp.Operator) -> str:
    3622        return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})")

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

    3624    def toarray_sql(self, expression: exp.ToArray) -> str:
    3625        arg = expression.this
    3626        if not arg.type:
    3627            from sqlglot.optimizer.annotate_types import annotate_types
    3628
    3629            arg = annotate_types(arg)
    3630
    3631        if arg.is_type(exp.DataType.Type.ARRAY):
    3632            return self.sql(arg)
    3633
    3634        cond_for_null = arg.is_(exp.null())
    3635        return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))

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

    3637    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3638        this = expression.this
    3639        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3640            return self.sql(this)
    3641
    3642        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

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

    3644    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3645        this = expression.this
    3646        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3647            return self.sql(this)
    3648
    3649        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP))

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

    3651    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3652        this = expression.this
    3653        time_format = self.format_time(expression)
    3654
    3655        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3656            return self.sql(
    3657                exp.cast(
    3658                    exp.StrToTime(this=this, format=expression.args["format"]),
    3659                    exp.DataType.Type.DATE,
    3660                )
    3661            )
    3662
    3663        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3664            return self.sql(this)
    3665
    3666        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

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

    3668    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3669        return self.sql(
    3670            exp.func(
    3671                "DATEDIFF",
    3672                expression.this,
    3673                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    3674                "day",
    3675            )
    3676        )

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

    3678    def lastday_sql(self, expression: exp.LastDay) -> str:
    3679        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3680            return self.function_fallback_sql(expression)
    3681
    3682        unit = expression.text("unit")
    3683        if unit and unit != "MONTH":
    3684            self.unsupported("Date parts are not supported in LAST_DAY.")
    3685
    3686        return self.func("LAST_DAY", expression.this)

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

    3688    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    3689        from sqlglot.dialects.dialect import unit_to_str
    3690
    3691        return self.func(
    3692            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    3693        )

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

    3695    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3696        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3697            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3698            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3699            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3700            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3701
    3702        from sqlglot.dialects import Dialect
    3703
    3704        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3705        if self.dialect.__class__ != Dialect:
    3706            self.unsupported("ARRAY_ANY is unsupported")
    3707
    3708        return self.function_fallback_sql(expression)

def generateseries_sql(self, expression: sqlglot.expressions.GenerateSeries) -> str: View Source

    3710    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3711        expression.set("is_end_exclusive", None)
    3712        return self.function_fallback_sql(expression)

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

    3714    def struct_sql(self, expression: exp.Struct) -> str:
    3715        expression.set(
    3716            "expressions",
    3717            [
    3718                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    3719                if isinstance(e, exp.PropertyEQ)
    3720                else e
    3721                for e in expression.expressions
    3722            ],
    3723        )
    3724
    3725        return self.function_fallback_sql(expression)

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

    3727    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3728        low = self.sql(expression, "this")
    3729        high = self.sql(expression, "expression")
    3730
    3731        return f"{low} TO {high}"

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

    3733    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3734        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3735        tables = f" {self.expressions(expression)}"
    3736
    3737        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3738
    3739        on_cluster = self.sql(expression, "cluster")
    3740        on_cluster = f" {on_cluster}" if on_cluster else ""
    3741
    3742        identity = self.sql(expression, "identity")
    3743        identity = f" {identity} IDENTITY" if identity else ""
    3744
    3745        option = self.sql(expression, "option")
    3746        option = f" {option}" if option else ""
    3747
    3748        partition = self.sql(expression, "partition")
    3749        partition = f" {partition}" if partition else ""
    3750
    3751        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

    3755    def convert_sql(self, expression: exp.Convert) -> str:
    3756        to = expression.this
    3757        value = expression.expression
    3758        style = expression.args.get("style")
    3759        safe = expression.args.get("safe")
    3760        strict = expression.args.get("strict")
    3761
    3762        if not to or not value:
    3763            return ""
    3764
    3765        # Retrieve length of datatype and override to default if not specified
    3766        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3767            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    3768
    3769        transformed: t.Optional[exp.Expression] = None
    3770        cast = exp.Cast if strict else exp.TryCast
    3771
    3772        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    3773        if isinstance(style, exp.Literal) and style.is_int:
    3774            from sqlglot.dialects.tsql import TSQL
    3775
    3776            style_value = style.name
    3777            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    3778            if not converted_style:
    3779                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    3780
    3781            fmt = exp.Literal.string(converted_style)
    3782
    3783            if to.this == exp.DataType.Type.DATE:
    3784                transformed = exp.StrToDate(this=value, format=fmt)
    3785            elif to.this == exp.DataType.Type.DATETIME:
    3786                transformed = exp.StrToTime(this=value, format=fmt)
    3787            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3788                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    3789            elif to.this == exp.DataType.Type.TEXT:
    3790                transformed = exp.TimeToStr(this=value, format=fmt)
    3791
    3792        if not transformed:
    3793            transformed = cast(this=value, to=to, safe=safe)
    3794
    3795        return self.sql(transformed)

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

    3851    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    3852        option = self.sql(expression, "this")
    3853
    3854        if option.upper() == "FILE_FORMAT":
    3855            values = self.expressions(expression, key="expression", flat=True, sep=" ")
    3856            return f"{option} = ({values})"
    3857
    3858        value = self.sql(expression, "expression")
    3859
    3860        if not value:
    3861            return option
    3862
    3863        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    3864
    3865        return f"{option}{op}{value}"

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

    3867    def credentials_sql(self, expression: exp.Credentials) -> str:
    3868        cred_expr = expression.args.get("credentials")
    3869        if isinstance(cred_expr, exp.Literal):
    3870            # Redshift case: CREDENTIALS <string>
    3871            credentials = self.sql(expression, "credentials")
    3872            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    3873        else:
    3874            # Snowflake case: CREDENTIALS = (...)
    3875            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    3876            credentials = f"CREDENTIALS = ({credentials})" if credentials else ""
    3877
    3878        storage = self.sql(expression, "storage")
    3879
    3880        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    3881        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    3882
    3883        iam_role = self.sql(expression, "iam_role")
    3884        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    3885
    3886        region = self.sql(expression, "region")
    3887        region = f" REGION {region}" if region else ""
    3888
    3889        return f"{credentials}{storage}{encryption}{iam_role}{region}"

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

    3891    def copy_sql(self, expression: exp.Copy) -> str:
    3892        this = self.sql(expression, "this")
    3893        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    3894
    3895        credentials = self.sql(expression, "credentials")
    3896        credentials = self.seg(credentials) if credentials else ""
    3897        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    3898        files = self.expressions(expression, key="files", flat=True)
    3899
    3900        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    3901        params = self.expressions(
    3902            expression,
    3903            key="params",
    3904            sep=sep,
    3905            new_line=True,
    3906            skip_last=True,
    3907            skip_first=True,
    3908            indent=self.COPY_PARAMS_ARE_WRAPPED,
    3909        )
    3910
    3911        if params:
    3912            if self.COPY_PARAMS_ARE_WRAPPED:
    3913                params = f" WITH ({params})"
    3914            elif not self.pretty:
    3915                params = f" {params}"
    3916
    3917        return f"COPY{this}{kind} {files}{credentials}{params}"

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

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

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

    3922    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    3923        on_sql = "ON" if expression.args.get("on") else "OFF"
    3924        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    3925        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    3926        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    3927        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    3928
    3929        if filter_col or retention_period:
    3930            on_sql = self.func("ON", filter_col, retention_period)
    3931
    3932        return f"DATA_DELETION={on_sql}"