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

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

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

###### Arguments:

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

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

    566    def __init__(
    567        self,
    568        pretty: t.Optional[bool] = None,
    569        identify: str | bool = False,
    570        normalize: bool = False,
    571        pad: int = 2,
    572        indent: int = 2,
    573        normalize_functions: t.Optional[str | bool] = None,
    574        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    575        max_unsupported: int = 3,
    576        leading_comma: bool = False,
    577        max_text_width: int = 80,
    578        comments: bool = True,
    579        dialect: DialectType = None,
    580    ):
    581        import sqlglot
    582        from sqlglot.dialects import Dialect
    583
    584        self.pretty = pretty if pretty is not None else sqlglot.pretty
    585        self.identify = identify
    586        self.normalize = normalize
    587        self.pad = pad
    588        self._indent = indent
    589        self.unsupported_level = unsupported_level
    590        self.max_unsupported = max_unsupported
    591        self.leading_comma = leading_comma
    592        self.max_text_width = max_text_width
    593        self.comments = comments
    594        self.dialect = Dialect.get_or_raise(dialect)
    595
    596        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    597        self.normalize_functions = (
    598            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    599        )
    600
    601        self.unsupported_messages: t.List[str] = []
    602        self._escaped_quote_end: str = (
    603            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    604        )
    605        self._escaped_identifier_end: str = (
    606            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
    607        )
    608
    609        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.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.TagColumnConstraint'>: <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_SET_OP = 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
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathSubscript'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
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.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <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.SetOperation'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.NVARCHAR: 'NVARCHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.CHAR: 'CHAR'>, <Type.VARCHAR: 'VARCHAR'>}
EXPRESSIONS_WITHOUT_NESTED_CTES: Set[Type[sqlglot.expressions.Expression]] = set()
SENTINEL_LINE_BREAK = '__SQLGLOT__LB__'
pretty
identify
normalize
pad
unsupported_level
max_unsupported
leading_comma
max_text_width
comments
dialect
normalize_functions
unsupported_messages: List[str]
def generate( self, expression: sqlglot.expressions.Expression, copy: bool = True) -> str: View Source

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

    645    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    646        """Apply generic preprocessing transformations to a given expression."""
    647        if (
    648            not expression.parent
    649            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    650            and any(node.parent is not expression for node in expression.find_all(exp.With))
    651        ):
    652            from sqlglot.transforms import move_ctes_to_top_level
    653
    654            expression = move_ctes_to_top_level(expression)
    655
    656        if self.ENSURE_BOOLS:
    657            from sqlglot.transforms import ensure_bools
    658
    659            expression = ensure_bools(expression)
    660
    661        return expression

Apply generic preprocessing transformations to a given expression.

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

    663    def unsupported(self, message: str) -> None:
    664        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    665            raise UnsupportedError(message)
    666        self.unsupported_messages.append(message)

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

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

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

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

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

    674    def pad_comment(self, comment: str) -> str:
    675        comment = " " + comment if comment[0].strip() else comment
    676        comment = comment + " " if comment[-1].strip() else comment
    677        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

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

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

    713    def wrap(self, expression: exp.Expression | str) -> str:
    714        this_sql = (
    715            self.sql(expression)
    716            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    717            else self.sql(expression, "this")
    718        )
    719        if not this_sql:
    720            return "()"
    721
    722        this_sql = self.indent(this_sql, level=1, pad=0)
    723        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    725    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    726        original = self.identify
    727        self.identify = False
    728        result = func(*args, **kwargs)
    729        self.identify = original
    730        return result

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

    732    def normalize_func(self, name: str) -> str:
    733        if self.normalize_functions == "upper" or self.normalize_functions is True:
    734            return name.upper()
    735        if self.normalize_functions == "lower":
    736            return name.lower()
    737        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

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

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

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

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

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

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

    805    def cache_sql(self, expression: exp.Cache) -> str:
    806        lazy = " LAZY" if expression.args.get("lazy") else ""
    807        table = self.sql(expression, "this")
    808        options = expression.args.get("options")
    809        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    810        sql = self.sql(expression, "expression")
    811        sql = f" AS{self.sep()}{sql}" if sql else ""
    812        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    813        return self.prepend_ctes(expression, sql)

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

    815    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    816        if isinstance(expression.parent, exp.Cast):
    817            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    818        default = "DEFAULT " if expression.args.get("default") else ""
    819        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    821    def column_parts(self, expression: exp.Column) -> str:
    822        return ".".join(
    823            self.sql(part)
    824            for part in (
    825                expression.args.get("catalog"),
    826                expression.args.get("db"),
    827                expression.args.get("table"),
    828                expression.args.get("this"),
    829            )
    830            if part
    831        )

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

    833    def column_sql(self, expression: exp.Column) -> str:
    834        join_mark = " (+)" if expression.args.get("join_mark") else ""
    835
    836        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
    837            join_mark = ""
    838            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    839
    840        return f"{self.column_parts(expression)}{join_mark}"

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

    842    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    843        this = self.sql(expression, "this")
    844        this = f" {this}" if this else ""
    845        position = self.sql(expression, "position")
    846        return f"{position}{this}"

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

    848    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    849        column = self.sql(expression, "this")
    850        kind = self.sql(expression, "kind")
    851        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    852        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    853        kind = f"{sep}{kind}" if kind else ""
    854        constraints = f" {constraints}" if constraints else ""
    855        position = self.sql(expression, "position")
    856        position = f" {position}" if position else ""
    857
    858        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    859            kind = ""
    860
    861        return f"{exists}{column}{kind}{constraints}{position}"

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

    863    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    864        this = self.sql(expression, "this")
    865        kind_sql = self.sql(expression, "kind").strip()
    866        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    868    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    869        this = self.sql(expression, "this")
    870        if expression.args.get("not_null"):
    871            persisted = " PERSISTED NOT NULL"
    872        elif expression.args.get("persisted"):
    873            persisted = " PERSISTED"
    874        else:
    875            persisted = ""
    876        return f"AS {this}{persisted}"

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

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

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

    881    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    882        if isinstance(expression.this, list):
    883            this = self.wrap(self.expressions(expression, key="this", flat=True))
    884        else:
    885            this = self.sql(expression, "this")
    886
    887        return f"COMPRESS {this}"

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

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

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

    922    def generatedasrowcolumnconstraint_sql(
    923        self, expression: exp.GeneratedAsRowColumnConstraint
    924    ) -> str:
    925        start = "START" if expression.args.get("start") else "END"
    926        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    927        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    929    def periodforsystemtimeconstraint_sql(
    930        self, expression: exp.PeriodForSystemTimeConstraint
    931    ) -> str:
    932        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

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

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

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

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

    940    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    941        desc = expression.args.get("desc")
    942        if desc is not None:
    943            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    944        return "PRIMARY KEY"

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

    946    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    947        this = self.sql(expression, "this")
    948        this = f" {this}" if this else ""
    949        index_type = expression.args.get("index_type")
    950        index_type = f" USING {index_type}" if index_type else ""
    951        on_conflict = self.sql(expression, "on_conflict")
    952        on_conflict = f" {on_conflict}" if on_conflict else ""
    953        return f"UNIQUE{this}{index_type}{on_conflict}"

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

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

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

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

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

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

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

    1076    def clone_sql(self, expression: exp.Clone) -> str:
    1077        this = self.sql(expression, "this")
    1078        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1079        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1080        return f"{shallow}{keyword} {this}"

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

    1082    def describe_sql(self, expression: exp.Describe) -> str:
    1083        style = expression.args.get("style")
    1084        style = f" {style}" if style else ""
    1085        return f"DESCRIBE{style} {self.sql(expression, 'this')}"

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

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

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

    1091    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1092        with_ = self.sql(expression, "with")
    1093        if with_:
    1094            sql = f"{with_}{self.sep()}{sql}"
    1095        return sql

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

    1097    def with_sql(self, expression: exp.With) -> str:
    1098        sql = self.expressions(expression, flat=True)
    1099        recursive = (
    1100            "RECURSIVE "
    1101            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1102            else ""
    1103        )
    1104
    1105        return f"WITH {recursive}{sql}"

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

    1107    def cte_sql(self, expression: exp.CTE) -> str:
    1108        alias = self.sql(expression, "alias")
    1109
    1110        materialized = expression.args.get("materialized")
    1111        if materialized is False:
    1112            materialized = "NOT MATERIALIZED "
    1113        elif materialized:
    1114            materialized = "MATERIALIZED "
    1115
    1116        return f"{alias} AS {materialized or ''}{self.wrap(expression)}"

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

    1118    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1119        alias = self.sql(expression, "this")
    1120        columns = self.expressions(expression, key="columns", flat=True)
    1121        columns = f"({columns})" if columns else ""
    1122
    1123        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1124            columns = ""
    1125            self.unsupported("Named columns are not supported in table alias.")
    1126
    1127        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1128            alias = self._next_name()
    1129
    1130        return f"{alias}{columns}"

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

    1132    def bitstring_sql(self, expression: exp.BitString) -> str:
    1133        this = self.sql(expression, "this")
    1134        if self.dialect.BIT_START:
    1135            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1136        return f"{int(this, 2)}"

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

    1138    def hexstring_sql(self, expression: exp.HexString) -> str:
    1139        this = self.sql(expression, "this")
    1140        if self.dialect.HEX_START:
    1141            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1142        return f"{int(this, 16)}"

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

    1144    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1145        this = self.sql(expression, "this")
    1146        if self.dialect.BYTE_START:
    1147            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1148        return this

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

    1150    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1151        this = self.sql(expression, "this")
    1152        escape = expression.args.get("escape")
    1153
    1154        if self.dialect.UNICODE_START:
    1155            escape_substitute = r"\\\1"
    1156            left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END
    1157        else:
    1158            escape_substitute = r"\\u\1"
    1159            left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END
    1160
    1161        if escape:
    1162            escape_pattern = re.compile(rf"{escape.name}(\d+)")
    1163            escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else ""
    1164        else:
    1165            escape_pattern = ESCAPED_UNICODE_RE
    1166            escape_sql = ""
    1167
    1168        if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE):
    1169            this = escape_pattern.sub(escape_substitute, this)
    1170
    1171        return f"{left_quote}{this}{right_quote}{escape_sql}"

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

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

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

    1177    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1178        this = self.sql(expression, "this")
    1179        specifier = self.sql(expression, "expression")
    1180        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1181        return f"{this}{specifier}"

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

    1183    def datatype_sql(self, expression: exp.DataType) -> str:
    1184        type_value = expression.this
    1185
    1186        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1187            type_sql = self.sql(expression, "kind")
    1188        else:
    1189            type_sql = (
    1190                self.TYPE_MAPPING.get(type_value, type_value.value)
    1191                if isinstance(type_value, exp.DataType.Type)
    1192                else type_value
    1193            )
    1194
    1195        nested = ""
    1196        interior = self.expressions(expression, flat=True)
    1197        values = ""
    1198
    1199        if interior:
    1200            if expression.args.get("nested"):
    1201                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1202                if expression.args.get("values") is not None:
    1203                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1204                    values = self.expressions(expression, key="values", flat=True)
    1205                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1206            elif type_value == exp.DataType.Type.INTERVAL:
    1207                nested = f" {interior}"
    1208            else:
    1209                nested = f"({interior})"
    1210
    1211        type_sql = f"{type_sql}{nested}{values}"
    1212        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1213            exp.DataType.Type.TIMETZ,
    1214            exp.DataType.Type.TIMESTAMPTZ,
    1215        ):
    1216            type_sql = f"{type_sql} WITH TIME ZONE"
    1217
    1218        return type_sql

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

    1220    def directory_sql(self, expression: exp.Directory) -> str:
    1221        local = "LOCAL " if expression.args.get("local") else ""
    1222        row_format = self.sql(expression, "row_format")
    1223        row_format = f" {row_format}" if row_format else ""
    1224        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1226    def delete_sql(self, expression: exp.Delete) -> str:
    1227        this = self.sql(expression, "this")
    1228        this = f" FROM {this}" if this else ""
    1229        using = self.sql(expression, "using")
    1230        using = f" USING {using}" if using else ""
    1231        where = self.sql(expression, "where")
    1232        returning = self.sql(expression, "returning")
    1233        limit = self.sql(expression, "limit")
    1234        tables = self.expressions(expression, key="tables")
    1235        tables = f" {tables}" if tables else ""
    1236        if self.RETURNING_END:
    1237            expression_sql = f"{this}{using}{where}{returning}{limit}"
    1238        else:
    1239            expression_sql = f"{returning}{this}{using}{where}{limit}"
    1240        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1242    def drop_sql(self, expression: exp.Drop) -> str:
    1243        this = self.sql(expression, "this")
    1244        expressions = self.expressions(expression, flat=True)
    1245        expressions = f" ({expressions})" if expressions else ""
    1246        kind = expression.args["kind"]
    1247        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1248        on_cluster = self.sql(expression, "cluster")
    1249        on_cluster = f" {on_cluster}" if on_cluster else ""
    1250        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1251        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1252        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1253        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1254        purge = " PURGE" if expression.args.get("purge") else ""
    1255        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

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

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

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

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

    1263    def fetch_sql(self, expression: exp.Fetch) -> str:
    1264        direction = expression.args.get("direction")
    1265        direction = f" {direction}" if direction else ""
    1266        count = expression.args.get("count")
    1267        count = f" {count}" if count else ""
    1268        if expression.args.get("percent"):
    1269            count = f"{count} PERCENT"
    1270        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1271        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1273    def filter_sql(self, expression: exp.Filter) -> str:
    1274        if self.AGGREGATE_FILTER_SUPPORTED:
    1275            this = self.sql(expression, "this")
    1276            where = self.sql(expression, "expression").strip()
    1277            return f"{this} FILTER({where})"
    1278
    1279        agg = expression.this
    1280        agg_arg = agg.this
    1281        cond = expression.expression.this
    1282        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1283        return self.sql(agg)

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

    1285    def hint_sql(self, expression: exp.Hint) -> str:
    1286        if not self.QUERY_HINTS:
    1287            self.unsupported("Hints are not supported")
    1288            return ""
    1289
    1290        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1292    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1293        using = self.sql(expression, "using")
    1294        using = f" USING {using}" if using else ""
    1295        columns = self.expressions(expression, key="columns", flat=True)
    1296        columns = f"({columns})" if columns else ""
    1297        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1298        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1299        where = self.sql(expression, "where")
    1300        include = self.expressions(expression, key="include", flat=True)
    1301        if include:
    1302            include = f" INCLUDE ({include})"
    1303        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1304        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1305        tablespace = self.sql(expression, "tablespace")
    1306        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1307        on = self.sql(expression, "on")
    1308        on = f" ON {on}" if on else ""
    1309
    1310        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"

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

    1312    def index_sql(self, expression: exp.Index) -> str:
    1313        unique = "UNIQUE " if expression.args.get("unique") else ""
    1314        primary = "PRIMARY " if expression.args.get("primary") else ""
    1315        amp = "AMP " if expression.args.get("amp") else ""
    1316        name = self.sql(expression, "this")
    1317        name = f"{name} " if name else ""
    1318        table = self.sql(expression, "table")
    1319        table = f"{self.INDEX_ON} {table}" if table else ""
    1320
    1321        index = "INDEX " if not table else ""
    1322
    1323        params = self.sql(expression, "params")
    1324        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1326    def identifier_sql(self, expression: exp.Identifier) -> str:
    1327        text = expression.name
    1328        lower = text.lower()
    1329        text = lower if self.normalize and not expression.quoted else text
    1330        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1331        if (
    1332            expression.quoted
    1333            or self.dialect.can_identify(text, self.identify)
    1334            or lower in self.RESERVED_KEYWORDS
    1335            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1336        ):
    1337            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1338        return text

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

    1340    def hex_sql(self, expression: exp.Hex) -> str:
    1341        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1342        if self.dialect.HEX_LOWERCASE:
    1343            text = self.func("LOWER", text)
    1344
    1345        return text

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

    1347    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1348        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1349        if not self.dialect.HEX_LOWERCASE:
    1350            text = self.func("LOWER", text)
    1351        return text

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

    1353    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1354        input_format = self.sql(expression, "input_format")
    1355        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1356        output_format = self.sql(expression, "output_format")
    1357        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1358        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1367    def properties_sql(self, expression: exp.Properties) -> str:
    1368        root_properties = []
    1369        with_properties = []
    1370
    1371        for p in expression.expressions:
    1372            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1373            if p_loc == exp.Properties.Location.POST_WITH:
    1374                with_properties.append(p)
    1375            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1376                root_properties.append(p)
    1377
    1378        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1379        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1380
    1381        if root_props and with_props and not self.pretty:
    1382            with_props = " " + with_props
    1383
    1384        return root_props + with_props

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

    1386    def root_properties(self, properties: exp.Properties) -> str:
    1387        if properties.expressions:
    1388            return self.expressions(properties, indent=False, sep=" ")
    1389        return ""

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

    1391    def properties(
    1392        self,
    1393        properties: exp.Properties,
    1394        prefix: str = "",
    1395        sep: str = ", ",
    1396        suffix: str = "",
    1397        wrapped: bool = True,
    1398    ) -> str:
    1399        if properties.expressions:
    1400            expressions = self.expressions(properties, sep=sep, indent=False)
    1401            if expressions:
    1402                expressions = self.wrap(expressions) if wrapped else expressions
    1403                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1404        return ""

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

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

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

    1409    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1410        properties_locs = defaultdict(list)
    1411        for p in properties.expressions:
    1412            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1413            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1414                properties_locs[p_loc].append(p)
    1415            else:
    1416                self.unsupported(f"Unsupported property {p.key}")
    1417
    1418        return properties_locs

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

    1420    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1421        if isinstance(expression.this, exp.Dot):
    1422            return self.sql(expression, "this")
    1423        return f"'{expression.name}'" if string_key else expression.name

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

    1425    def property_sql(self, expression: exp.Property) -> str:
    1426        property_cls = expression.__class__
    1427        if property_cls == exp.Property:
    1428            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1429
    1430        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1431        if not property_name:
    1432            self.unsupported(f"Unsupported property {expression.key}")
    1433
    1434        return f"{property_name}={self.sql(expression, 'this')}"

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

    1436    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1437        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1438            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1439            options = f" {options}" if options else ""
    1440
    1441            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1442            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1443                like = f"({like})"
    1444
    1445            return like
    1446
    1447        if expression.expressions:
    1448            self.unsupported("Transpilation of LIKE property options is unsupported")
    1449
    1450        select = exp.select("*").from_(expression.this).limit(0)
    1451        return f"AS {self.sql(select)}"

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

    1453    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1454        no = "NO " if expression.args.get("no") else ""
    1455        protection = " PROTECTION" if expression.args.get("protection") else ""
    1456        return f"{no}FALLBACK{protection}"

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

    1458    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1459        no = "NO " if expression.args.get("no") else ""
    1460        local = expression.args.get("local")
    1461        local = f"{local} " if local else ""
    1462        dual = "DUAL " if expression.args.get("dual") else ""
    1463        before = "BEFORE " if expression.args.get("before") else ""
    1464        after = "AFTER " if expression.args.get("after") else ""
    1465        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1467    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1468        freespace = self.sql(expression, "this")
    1469        percent = " PERCENT" if expression.args.get("percent") else ""
    1470        return f"FREESPACE={freespace}{percent}"

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

    1472    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1473        if expression.args.get("default"):
    1474            property = "DEFAULT"
    1475        elif expression.args.get("on"):
    1476            property = "ON"
    1477        else:
    1478            property = "OFF"
    1479        return f"CHECKSUM={property}"

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

    1481    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1482        if expression.args.get("no"):
    1483            return "NO MERGEBLOCKRATIO"
    1484        if expression.args.get("default"):
    1485            return "DEFAULT MERGEBLOCKRATIO"
    1486
    1487        percent = " PERCENT" if expression.args.get("percent") else ""
    1488        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1490    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1491        default = expression.args.get("default")
    1492        minimum = expression.args.get("minimum")
    1493        maximum = expression.args.get("maximum")
    1494        if default or minimum or maximum:
    1495            if default:
    1496                prop = "DEFAULT"
    1497            elif minimum:
    1498                prop = "MINIMUM"
    1499            else:
    1500                prop = "MAXIMUM"
    1501            return f"{prop} DATABLOCKSIZE"
    1502        units = expression.args.get("units")
    1503        units = f" {units}" if units else ""
    1504        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1506    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1507        autotemp = expression.args.get("autotemp")
    1508        always = expression.args.get("always")
    1509        default = expression.args.get("default")
    1510        manual = expression.args.get("manual")
    1511        never = expression.args.get("never")
    1512
    1513        if autotemp is not None:
    1514            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1515        elif always:
    1516            prop = "ALWAYS"
    1517        elif default:
    1518            prop = "DEFAULT"
    1519        elif manual:
    1520            prop = "MANUAL"
    1521        elif never:
    1522            prop = "NEVER"
    1523        return f"BLOCKCOMPRESSION={prop}"

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

    1525    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1526        no = expression.args.get("no")
    1527        no = " NO" if no else ""
    1528        concurrent = expression.args.get("concurrent")
    1529        concurrent = " CONCURRENT" if concurrent else ""
    1530        target = self.sql(expression, "target")
    1531        target = f" {target}" if target else ""
    1532        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1534    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1535        if isinstance(expression.this, list):
    1536            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1537        if expression.this:
    1538            modulus = self.sql(expression, "this")
    1539            remainder = self.sql(expression, "expression")
    1540            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1541
    1542        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1543        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1544        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1546    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1547        this = self.sql(expression, "this")
    1548
    1549        for_values_or_default = expression.expression
    1550        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1551            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1552        else:
    1553            for_values_or_default = " DEFAULT"
    1554
    1555        return f"PARTITION OF {this}{for_values_or_default}"

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

    1557    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1558        kind = expression.args.get("kind")
    1559        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1560        for_or_in = expression.args.get("for_or_in")
    1561        for_or_in = f" {for_or_in}" if for_or_in else ""
    1562        lock_type = expression.args.get("lock_type")
    1563        override = " OVERRIDE" if expression.args.get("override") else ""
    1564        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1566    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1567        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1568        statistics = expression.args.get("statistics")
    1569        statistics_sql = ""
    1570        if statistics is not None:
    1571            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1572        return f"{data_sql}{statistics_sql}"

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

    1574    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1575        this = self.sql(expression, "this")
    1576        this = f"HISTORY_TABLE={this}" if this else ""
    1577        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1578        data_consistency = (
    1579            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1580        )
    1581        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1582        retention_period = (
    1583            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1584        )
    1585
    1586        if this:
    1587            on_sql = self.func("ON", this, data_consistency, retention_period)
    1588        else:
    1589            on_sql = "ON" if expression.args.get("on") else "OFF"
    1590
    1591        sql = f"SYSTEM_VERSIONING={on_sql}"
    1592
    1593        return f"WITH({sql})" if expression.args.get("with") else sql

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

    1595    def insert_sql(self, expression: exp.Insert) -> str:
    1596        hint = self.sql(expression, "hint")
    1597        overwrite = expression.args.get("overwrite")
    1598
    1599        if isinstance(expression.this, exp.Directory):
    1600            this = " OVERWRITE" if overwrite else " INTO"
    1601        else:
    1602            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1603
    1604        stored = self.sql(expression, "stored")
    1605        stored = f" {stored}" if stored else ""
    1606        alternative = expression.args.get("alternative")
    1607        alternative = f" OR {alternative}" if alternative else ""
    1608        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1609        is_function = expression.args.get("is_function")
    1610        if is_function:
    1611            this = f"{this} FUNCTION"
    1612        this = f"{this} {self.sql(expression, 'this')}"
    1613
    1614        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1615        where = self.sql(expression, "where")
    1616        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1617        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1618        on_conflict = self.sql(expression, "conflict")
    1619        on_conflict = f" {on_conflict}" if on_conflict else ""
    1620        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1621        returning = self.sql(expression, "returning")
    1622
    1623        if self.RETURNING_END:
    1624            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1625        else:
    1626            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1627
    1628        sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}"
    1629        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

    1640    def kill_sql(self, expression: exp.Kill) -> str:
    1641        kind = self.sql(expression, "kind")
    1642        kind = f" {kind}" if kind else ""
    1643        this = self.sql(expression, "this")
    1644        this = f" {this}" if this else ""
    1645        return f"KILL{kind}{this}"

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

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

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

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

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

    1653    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1654        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1655
    1656        constraint = self.sql(expression, "constraint")
    1657        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1658
    1659        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1660        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1661        action = self.sql(expression, "action")
    1662
    1663        expressions = self.expressions(expression, flat=True)
    1664        if expressions:
    1665            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1666            expressions = f" {set_keyword}{expressions}"
    1667
    1668        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"

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

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

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

    1673    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1674        fields = self.sql(expression, "fields")
    1675        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1676        escaped = self.sql(expression, "escaped")
    1677        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1678        items = self.sql(expression, "collection_items")
    1679        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1680        keys = self.sql(expression, "map_keys")
    1681        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1682        lines = self.sql(expression, "lines")
    1683        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1684        null = self.sql(expression, "null")
    1685        null = f" NULL DEFINED AS {null}" if null else ""
    1686        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1691    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1692        this = f"{self.sql(expression, 'this')} INDEX"
    1693        target = self.sql(expression, "target")
    1694        target = f" FOR {target}" if target else ""
    1695        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1697    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1698        this = self.sql(expression, "this")
    1699        kind = self.sql(expression, "kind")
    1700        expr = self.sql(expression, "expression")
    1701        return f"{this} ({kind} => {expr})"

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

    1703    def table_parts(self, expression: exp.Table) -> str:
    1704        return ".".join(
    1705            self.sql(part)
    1706            for part in (
    1707                expression.args.get("catalog"),
    1708                expression.args.get("db"),
    1709                expression.args.get("this"),
    1710            )
    1711            if part is not None
    1712        )

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

    1714    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1715        table = self.table_parts(expression)
    1716        only = "ONLY " if expression.args.get("only") else ""
    1717        partition = self.sql(expression, "partition")
    1718        partition = f" {partition}" if partition else ""
    1719        version = self.sql(expression, "version")
    1720        version = f" {version}" if version else ""
    1721        alias = self.sql(expression, "alias")
    1722        alias = f"{sep}{alias}" if alias else ""
    1723        hints = self.expressions(expression, key="hints", sep=" ")
    1724        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1725        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1726        pivots = f" {pivots}" if pivots else ""
    1727        joins = self.indent(
    1728            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1729        )
    1730        laterals = self.expressions(expression, key="laterals", sep="")
    1731
    1732        file_format = self.sql(expression, "format")
    1733        if file_format:
    1734            pattern = self.sql(expression, "pattern")
    1735            pattern = f", PATTERN => {pattern}" if pattern else ""
    1736            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1737
    1738        ordinality = expression.args.get("ordinality") or ""
    1739        if ordinality:
    1740            ordinality = f" WITH ORDINALITY{alias}"
    1741            alias = ""
    1742
    1743        when = self.sql(expression, "when")
    1744        if when:
    1745            table = f"{table} {when}"
    1746
    1747        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

    1749    def tablesample_sql(
    1750        self,
    1751        expression: exp.TableSample,
    1752        sep: str = " AS ",
    1753        tablesample_keyword: t.Optional[str] = None,
    1754    ) -> str:
    1755        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1756            table = expression.this.copy()
    1757            table.set("alias", None)
    1758            this = self.sql(table)
    1759            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1760        else:
    1761            this = self.sql(expression, "this")
    1762            alias = ""
    1763
    1764        method = self.sql(expression, "method")
    1765        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1766        numerator = self.sql(expression, "bucket_numerator")
    1767        denominator = self.sql(expression, "bucket_denominator")
    1768        field = self.sql(expression, "bucket_field")
    1769        field = f" ON {field}" if field else ""
    1770        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1771        seed = self.sql(expression, "seed")
    1772        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1773
    1774        size = self.sql(expression, "size")
    1775        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1776            size = f"{size} ROWS"
    1777
    1778        percent = self.sql(expression, "percent")
    1779        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1780            percent = f"{percent} PERCENT"
    1781
    1782        expr = f"{bucket}{percent}{size}"
    1783        if self.TABLESAMPLE_REQUIRES_PARENS:
    1784            expr = f"({expr})"
    1785
    1786        return (
    1787            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
    1788        )

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

    1790    def pivot_sql(self, expression: exp.Pivot) -> str:
    1791        expressions = self.expressions(expression, flat=True)
    1792
    1793        if expression.this:
    1794            this = self.sql(expression, "this")
    1795            if not expressions:
    1796                return f"UNPIVOT {this}"
    1797
    1798            on = f"{self.seg('ON')} {expressions}"
    1799            using = self.expressions(expression, key="using", flat=True)
    1800            using = f"{self.seg('USING')} {using}" if using else ""
    1801            group = self.sql(expression, "group")
    1802            return f"PIVOT {this}{on}{using}{group}"
    1803
    1804        alias = self.sql(expression, "alias")
    1805        alias = f" AS {alias}" if alias else ""
    1806        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    1807        field = self.sql(expression, "field")
    1808        include_nulls = expression.args.get("include_nulls")
    1809        if include_nulls is not None:
    1810            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1811        else:
    1812            nulls = ""
    1813        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1815    def version_sql(self, expression: exp.Version) -> str:
    1816        this = f"FOR {expression.name}"
    1817        kind = expression.text("kind")
    1818        expr = self.sql(expression, "expression")
    1819        return f"{this} {kind} {expr}"

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

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

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

    1824    def update_sql(self, expression: exp.Update) -> str:
    1825        this = self.sql(expression, "this")
    1826        set_sql = self.expressions(expression, flat=True)
    1827        from_sql = self.sql(expression, "from")
    1828        where_sql = self.sql(expression, "where")
    1829        returning = self.sql(expression, "returning")
    1830        order = self.sql(expression, "order")
    1831        limit = self.sql(expression, "limit")
    1832        if self.RETURNING_END:
    1833            expression_sql = f"{from_sql}{where_sql}{returning}"
    1834        else:
    1835            expression_sql = f"{returning}{from_sql}{where_sql}"
    1836        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1837        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

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

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

    1892    def group_sql(self, expression: exp.Group) -> str:
    1893        group_by_all = expression.args.get("all")
    1894        if group_by_all is True:
    1895            modifier = " ALL"
    1896        elif group_by_all is False:
    1897            modifier = " DISTINCT"
    1898        else:
    1899            modifier = ""
    1900
    1901        group_by = self.op_expressions(f"GROUP BY{modifier}", expression)
    1902
    1903        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1904        grouping_sets = (
    1905            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1906        )
    1907
    1908        cube = expression.args.get("cube", [])
    1909        if seq_get(cube, 0) is True:
    1910            return f"{group_by}{self.seg('WITH CUBE')}"
    1911        else:
    1912            cube_sql = self.expressions(expression, key="cube", indent=False)
    1913            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1914
    1915        rollup = expression.args.get("rollup", [])
    1916        if seq_get(rollup, 0) is True:
    1917            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1918        else:
    1919            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1920            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1921
    1922        groupings = csv(
    1923            grouping_sets,
    1924            cube_sql,
    1925            rollup_sql,
    1926            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1927            sep=self.GROUPINGS_SEP,
    1928        )
    1929
    1930        if expression.args.get("expressions") and groupings:
    1931            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1932
    1933        return f"{group_by}{groupings}"

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

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

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

    1939    def connect_sql(self, expression: exp.Connect) -> str:
    1940        start = self.sql(expression, "start")
    1941        start = self.seg(f"START WITH {start}") if start else ""
    1942        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    1943        connect = self.sql(expression, "connect")
    1944        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    1945        return start + connect

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

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

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

    1950    def join_sql(self, expression: exp.Join) -> str:
    1951        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1952            side = None
    1953        else:
    1954            side = expression.side
    1955
    1956        op_sql = " ".join(
    1957            op
    1958            for op in (
    1959                expression.method,
    1960                "GLOBAL" if expression.args.get("global") else None,
    1961                side,
    1962                expression.kind,
    1963                expression.hint if self.JOIN_HINTS else None,
    1964            )
    1965            if op
    1966        )
    1967        match_cond = self.sql(expression, "match_condition")
    1968        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    1969        on_sql = self.sql(expression, "on")
    1970        using = expression.args.get("using")
    1971
    1972        if not on_sql and using:
    1973            on_sql = csv(*(self.sql(column) for column in using))
    1974
    1975        this = expression.this
    1976        this_sql = self.sql(this)
    1977
    1978        if on_sql:
    1979            on_sql = self.indent(on_sql, skip_first=True)
    1980            space = self.seg(" " * self.pad) if self.pretty else " "
    1981            if using:
    1982                on_sql = f"{space}USING ({on_sql})"
    1983            else:
    1984                on_sql = f"{space}ON {on_sql}"
    1985        elif not op_sql:
    1986            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    1987                return f" {this_sql}"
    1988
    1989            return f", {this_sql}"
    1990
    1991        if op_sql != "STRAIGHT_JOIN":
    1992            op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1993
    1994        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

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

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

    2001    def lateral_op(self, expression: exp.Lateral) -> str:
    2002        cross_apply = expression.args.get("cross_apply")
    2003
    2004        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2005        if cross_apply is True:
    2006            op = "INNER JOIN "
    2007        elif cross_apply is False:
    2008            op = "LEFT JOIN "
    2009        else:
    2010            op = ""
    2011
    2012        return f"{op}LATERAL"

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

    2014    def lateral_sql(self, expression: exp.Lateral) -> str:
    2015        this = self.sql(expression, "this")
    2016
    2017        if expression.args.get("view"):
    2018            alias = expression.args["alias"]
    2019            columns = self.expressions(alias, key="columns", flat=True)
    2020            table = f" {alias.name}" if alias.name else ""
    2021            columns = f" AS {columns}" if columns else ""
    2022            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2023            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2024
    2025        alias = self.sql(expression, "alias")
    2026        alias = f" AS {alias}" if alias else ""
    2027        return f"{self.lateral_op(expression)} {this}{alias}"

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

    2029    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2030        this = self.sql(expression, "this")
    2031
    2032        args = [
    2033            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2034            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2035            if e
    2036        ]
    2037
    2038        args_sql = ", ".join(self.sql(e) for e in args)
    2039        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2040        expressions = self.expressions(expression, flat=True)
    2041        expressions = f" BY {expressions}" if expressions else ""
    2042
    2043        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    2045    def offset_sql(self, expression: exp.Offset) -> str:
    2046        this = self.sql(expression, "this")
    2047        value = expression.expression
    2048        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2049        expressions = self.expressions(expression, flat=True)
    2050        expressions = f" BY {expressions}" if expressions else ""
    2051        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    2053    def setitem_sql(self, expression: exp.SetItem) -> str:
    2054        kind = self.sql(expression, "kind")
    2055        kind = f"{kind} " if kind else ""
    2056        this = self.sql(expression, "this")
    2057        expressions = self.expressions(expression)
    2058        collate = self.sql(expression, "collate")
    2059        collate = f" COLLATE {collate}" if collate else ""
    2060        global_ = "GLOBAL " if expression.args.get("global") else ""
    2061        return f"{global_}{kind}{this}{expressions}{collate}"

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

    2063    def set_sql(self, expression: exp.Set) -> str:
    2064        expressions = (
    2065            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2066        )
    2067        tag = " TAG" if expression.args.get("tag") else ""
    2068        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    2073    def lock_sql(self, expression: exp.Lock) -> str:
    2074        if not self.LOCKING_READS_SUPPORTED:
    2075            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2076            return ""
    2077
    2078        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2079        expressions = self.expressions(expression, flat=True)
    2080        expressions = f" OF {expressions}" if expressions else ""
    2081        wait = expression.args.get("wait")
    2082
    2083        if wait is not None:
    2084            if isinstance(wait, exp.Literal):
    2085                wait = f" WAIT {self.sql(wait)}"
    2086            else:
    2087                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2088
    2089        return f"{lock_type}{expressions}{wait or ''}"

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

    2091    def literal_sql(self, expression: exp.Literal) -> str:
    2092        text = expression.this or ""
    2093        if expression.is_string:
    2094            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2095        return text

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

    2097    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2098        if self.dialect.ESCAPED_SEQUENCES:
    2099            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2100            text = "".join(
    2101                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2102            )
    2103
    2104        return self._replace_line_breaks(text).replace(
    2105            self.dialect.QUOTE_END, self._escaped_quote_end
    2106        )

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

    2108    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2109        local = " LOCAL" if expression.args.get("local") else ""
    2110        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2111        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2112        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2113        partition = self.sql(expression, "partition")
    2114        partition = f" {partition}" if partition else ""
    2115        input_format = self.sql(expression, "input_format")
    2116        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2117        serde = self.sql(expression, "serde")
    2118        serde = f" SERDE {serde}" if serde else ""
    2119        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2121    def null_sql(self, *_) -> str:
    2122        return "NULL"

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

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

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

    2127    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2128        this = self.sql(expression, "this")
    2129        this = f"{this} " if this else this
    2130        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2131        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2132        interpolated_values = [
    2133            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
    2134            for named_expression in expression.args.get("interpolate") or []
    2135        ]
    2136        interpolate = (
    2137            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2138        )
    2139        return f"{order}{interpolate}"

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

    2141    def withfill_sql(self, expression: exp.WithFill) -> str:
    2142        from_sql = self.sql(expression, "from")
    2143        from_sql = f" FROM {from_sql}" if from_sql else ""
    2144        to_sql = self.sql(expression, "to")
    2145        to_sql = f" TO {to_sql}" if to_sql else ""
    2146        step_sql = self.sql(expression, "step")
    2147        step_sql = f" STEP {step_sql}" if step_sql else ""
    2148        return f"WITH FILL{from_sql}{to_sql}{step_sql}"

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

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

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

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

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

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

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

    2159    def ordered_sql(self, expression: exp.Ordered) -> str:
    2160        desc = expression.args.get("desc")
    2161        asc = not desc
    2162
    2163        nulls_first = expression.args.get("nulls_first")
    2164        nulls_last = not nulls_first
    2165        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2166        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2167        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2168
    2169        this = self.sql(expression, "this")
    2170
    2171        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2172        nulls_sort_change = ""
    2173        if nulls_first and (
    2174            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2175        ):
    2176            nulls_sort_change = " NULLS FIRST"
    2177        elif (
    2178            nulls_last
    2179            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2180            and not nulls_are_last
    2181        ):
    2182            nulls_sort_change = " NULLS LAST"
    2183
    2184        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2185        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2186            window = expression.find_ancestor(exp.Window, exp.Select)
    2187            if isinstance(window, exp.Window) and window.args.get("spec"):
    2188                self.unsupported(
    2189                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2190                )
    2191                nulls_sort_change = ""
    2192            elif self.NULL_ORDERING_SUPPORTED is None:
    2193                if expression.this.is_int:
    2194                    self.unsupported(
    2195                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2196                    )
    2197                elif not isinstance(expression.this, exp.Rand):
    2198                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2199                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2200                nulls_sort_change = ""
    2201
    2202        with_fill = self.sql(expression, "with_fill")
    2203        with_fill = f" {with_fill}" if with_fill else ""
    2204
    2205        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

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

    2207    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2208        window_frame = self.sql(expression, "window_frame")
    2209        window_frame = f"{window_frame} " if window_frame else ""
    2210
    2211        this = self.sql(expression, "this")
    2212
    2213        return f"{window_frame}{this}"

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

    2215    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2216        partition = self.partition_by_sql(expression)
    2217        order = self.sql(expression, "order")
    2218        measures = self.expressions(expression, key="measures")
    2219        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2220        rows = self.sql(expression, "rows")
    2221        rows = self.seg(rows) if rows else ""
    2222        after = self.sql(expression, "after")
    2223        after = self.seg(after) if after else ""
    2224        pattern = self.sql(expression, "pattern")
    2225        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2226        definition_sqls = [
    2227            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2228            for definition in expression.args.get("define", [])
    2229        ]
    2230        definitions = self.expressions(sqls=definition_sqls)
    2231        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2232        body = "".join(
    2233            (
    2234                partition,
    2235                order,
    2236                measures,
    2237                rows,
    2238                after,
    2239                pattern,
    2240                define,
    2241            )
    2242        )
    2243        alias = self.sql(expression, "alias")
    2244        alias = f" {alias}" if alias else ""
    2245        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2247    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2248        limit = expression.args.get("limit")
    2249
    2250        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2251            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2252        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2253            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2254
    2255        return csv(
    2256            *sqls,
    2257            *[self.sql(join) for join in expression.args.get("joins") or []],
    2258            self.sql(expression, "connect"),
    2259            self.sql(expression, "match"),
    2260            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2261            self.sql(expression, "prewhere"),
    2262            self.sql(expression, "where"),
    2263            self.sql(expression, "group"),
    2264            self.sql(expression, "having"),
    2265            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2266            self.sql(expression, "order"),
    2267            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2268            *self.after_limit_modifiers(expression),
    2269            self.options_modifier(expression),
    2270            sep="",
    2271        )

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

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

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

    2277    def queryoption_sql(self, expression: exp.QueryOption) -> str:
    2278        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

    2280    def offset_limit_modifiers(
    2281        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2282    ) -> t.List[str]:
    2283        return [
    2284            self.sql(expression, "offset") if fetch else self.sql(limit),
    2285            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2286        ]

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

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

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

    2293    def select_sql(self, expression: exp.Select) -> str:
    2294        into = expression.args.get("into")
    2295        if not self.SUPPORTS_SELECT_INTO and into:
    2296            into.pop()
    2297
    2298        hint = self.sql(expression, "hint")
    2299        distinct = self.sql(expression, "distinct")
    2300        distinct = f" {distinct}" if distinct else ""
    2301        kind = self.sql(expression, "kind")
    2302
    2303        limit = expression.args.get("limit")
    2304        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2305            top = self.limit_sql(limit, top=True)
    2306            limit.pop()
    2307        else:
    2308            top = ""
    2309
    2310        expressions = self.expressions(expression)
    2311
    2312        if kind:
    2313            if kind in self.SELECT_KINDS:
    2314                kind = f" AS {kind}"
    2315            else:
    2316                if kind == "STRUCT":
    2317                    expressions = self.expressions(
    2318                        sqls=[
    2319                            self.sql(
    2320                                exp.Struct(
    2321                                    expressions=[
    2322                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2323                                        if isinstance(e, exp.Alias)
    2324                                        else e
    2325                                        for e in expression.expressions
    2326                                    ]
    2327                                )
    2328                            )
    2329                        ]
    2330                    )
    2331                kind = ""
    2332
    2333        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2334        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2335        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2336        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2337        sql = self.query_modifiers(
    2338            expression,
    2339            f"SELECT{top_distinct}{kind}{expressions}",
    2340            self.sql(expression, "into", comment=False),
    2341            self.sql(expression, "from", comment=False),
    2342        )
    2343
    2344        sql = self.prepend_ctes(expression, sql)
    2345
    2346        if not self.SUPPORTS_SELECT_INTO and into:
    2347            if into.args.get("temporary"):
    2348                table_kind = " TEMPORARY"
    2349            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2350                table_kind = " UNLOGGED"
    2351            else:
    2352                table_kind = ""
    2353            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2354
    2355        return sql

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

    2357    def schema_sql(self, expression: exp.Schema) -> str:
    2358        this = self.sql(expression, "this")
    2359        sql = self.schema_columns_sql(expression)
    2360        return f"{this} {sql}" if this and sql else this or sql

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

    2362    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2363        if expression.expressions:
    2364            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2365        return ""

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

    2367    def star_sql(self, expression: exp.Star) -> str:
    2368        except_ = self.expressions(expression, key="except", flat=True)
    2369        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2370        replace = self.expressions(expression, key="replace", flat=True)
    2371        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2372        rename = self.expressions(expression, key="rename", flat=True)
    2373        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2374        return f"*{except_}{replace}{rename}"

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

    2376    def parameter_sql(self, expression: exp.Parameter) -> str:
    2377        this = self.sql(expression, "this")
    2378        return f"{self.PARAMETER_TOKEN}{this}"

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

    2380    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2381        this = self.sql(expression, "this")
    2382        kind = expression.text("kind")
    2383        if kind:
    2384            kind = f"{kind}."
    2385        return f"@@{kind}{this}"

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

    2387    def placeholder_sql(self, expression: exp.Placeholder) -> str:
    2388        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

    2390    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2391        alias = self.sql(expression, "alias")
    2392        alias = f"{sep}{alias}" if alias else ""
    2393
    2394        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2395        pivots = f" {pivots}" if pivots else ""
    2396
    2397        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2398        return self.prepend_ctes(expression, sql)

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

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

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

    2404    def set_operations(self, expression: exp.SetOperation) -> str:
    2405        if not self.SET_OP_MODIFIERS:
    2406            limit = expression.args.get("limit")
    2407            order = expression.args.get("order")
    2408
    2409            if limit or order:
    2410                select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    2411
    2412                if limit:
    2413                    select = select.limit(limit.pop(), copy=False)
    2414                if order:
    2415                    select = select.order_by(order.pop(), copy=False)
    2416                return self.sql(select)
    2417
    2418        sqls: t.List[str] = []
    2419        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    2420
    2421        while stack:
    2422            node = stack.pop()
    2423
    2424            if isinstance(node, exp.SetOperation):
    2425                stack.append(node.expression)
    2426                stack.append(
    2427                    self.maybe_comment(
    2428                        getattr(self, f"{node.key}_op")(node),
    2429                        comments=node.comments,
    2430                        separated=True,
    2431                    )
    2432                )
    2433                stack.append(node.this)
    2434            else:
    2435                sqls.append(self.sql(node))
    2436
    2437        this = self.sep().join(sqls)
    2438        this = self.query_modifiers(expression, this)
    2439        return self.prepend_ctes(expression, this)

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

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

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

    2444    def union_op(self, expression: exp.SetOperation) -> str:
    2445        kind = " DISTINCT" if self.EXPLICIT_SET_OP else ""
    2446        kind = kind if expression.args.get("distinct") else " ALL"
    2447        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2448        return f"UNION{kind}{by_name}"

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

    2450    def unnest_sql(self, expression: exp.Unnest) -> str:
    2451        args = self.expressions(expression, flat=True)
    2452
    2453        alias = expression.args.get("alias")
    2454        offset = expression.args.get("offset")
    2455
    2456        if self.UNNEST_WITH_ORDINALITY:
    2457            if alias and isinstance(offset, exp.Expression):
    2458                alias.append("columns", offset)
    2459
    2460        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2461            columns = alias.columns
    2462            alias = self.sql(columns[0]) if columns else ""
    2463        else:
    2464            alias = self.sql(alias)
    2465
    2466        alias = f" AS {alias}" if alias else alias
    2467        if self.UNNEST_WITH_ORDINALITY:
    2468            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2469        else:
    2470            if isinstance(offset, exp.Expression):
    2471                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2472            elif offset:
    2473                suffix = f"{alias} WITH OFFSET"
    2474            else:
    2475                suffix = alias
    2476
    2477        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2486    def window_sql(self, expression: exp.Window) -> str:
    2487        this = self.sql(expression, "this")
    2488        partition = self.partition_by_sql(expression)
    2489        order = expression.args.get("order")
    2490        order = self.order_sql(order, flat=True) if order else ""
    2491        spec = self.sql(expression, "spec")
    2492        alias = self.sql(expression, "alias")
    2493        over = self.sql(expression, "over") or "OVER"
    2494
    2495        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2496
    2497        first = expression.args.get("first")
    2498        if first is None:
    2499            first = ""
    2500        else:
    2501            first = "FIRST" if first else "LAST"
    2502
    2503        if not partition and not order and not spec and alias:
    2504            return f"{this} {alias}"
    2505
    2506        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2507        return f"{this} ({args})"

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

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

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

    2513    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2514        kind = self.sql(expression, "kind")
    2515        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2516        end = (
    2517            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2518            or "CURRENT ROW"
    2519        )
    2520        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2527    def between_sql(self, expression: exp.Between) -> str:
    2528        this = self.sql(expression, "this")
    2529        low = self.sql(expression, "low")
    2530        high = self.sql(expression, "high")
    2531        return f"{this} BETWEEN {low} AND {high}"

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

    2533    def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]:
    2534        return apply_index_offset(
    2535            expression.this,
    2536            expression.expressions,
    2537            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2538        )

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

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

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

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

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

    2548    def any_sql(self, expression: exp.Any) -> str:
    2549        this = self.sql(expression, "this")
    2550        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2551            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2552                this = self.wrap(this)
    2553            return f"ANY{this}"
    2554        return f"ANY {this}"

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

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

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

    2559    def case_sql(self, expression: exp.Case) -> str:
    2560        this = self.sql(expression, "this")
    2561        statements = [f"CASE {this}" if this else "CASE"]
    2562
    2563        for e in expression.args["ifs"]:
    2564            statements.append(f"WHEN {self.sql(e, 'this')}")
    2565            statements.append(f"THEN {self.sql(e, 'true')}")
    2566
    2567        default = self.sql(expression, "default")
    2568
    2569        if default:
    2570            statements.append(f"ELSE {default}")
    2571
    2572        statements.append("END")
    2573
    2574        if self.pretty and self.too_wide(statements):
    2575            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2576
    2577        return " ".join(statements)

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

    2579    def constraint_sql(self, expression: exp.Constraint) -> str:
    2580        this = self.sql(expression, "this")
    2581        expressions = self.expressions(expression, flat=True)
    2582        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2589    def extract_sql(self, expression: exp.Extract) -> str:
    2590        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2591        expression_sql = self.sql(expression, "expression")
    2592        return f"EXTRACT({this} FROM {expression_sql})"

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

    2594    def trim_sql(self, expression: exp.Trim) -> str:
    2595        trim_type = self.sql(expression, "position")
    2596
    2597        if trim_type == "LEADING":
    2598            return self.func("LTRIM", expression.this)
    2599        elif trim_type == "TRAILING":
    2600            return self.func("RTRIM", expression.this)
    2601        else:
    2602            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

    2604    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2605        args = expression.expressions
    2606        if isinstance(expression, exp.ConcatWs):
    2607            args = args[1:]  # Skip the delimiter
    2608
    2609        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2610            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2611
    2612        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2613            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2614
    2615        return args

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

    2617    def concat_sql(self, expression: exp.Concat) -> str:
    2618        expressions = self.convert_concat_args(expression)
    2619
    2620        # Some dialects don't allow a single-argument CONCAT call
    2621        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2622            return self.sql(expressions[0])
    2623
    2624        return self.func("CONCAT", *expressions)

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

    2626    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2627        return self.func(
    2628            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2629        )

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

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

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

    2635    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2636        expressions = self.expressions(expression, flat=True)
    2637        reference = self.sql(expression, "reference")
    2638        reference = f" {reference}" if reference else ""
    2639        delete = self.sql(expression, "delete")
    2640        delete = f" ON DELETE {delete}" if delete else ""
    2641        update = self.sql(expression, "update")
    2642        update = f" ON UPDATE {update}" if update else ""
    2643        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2645    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2646        expressions = self.expressions(expression, flat=True)
    2647        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2648        options = f" {options}" if options else ""
    2649        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

    2659    def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str:
    2660        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

    2662    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2663        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2664        if self.QUOTE_JSON_PATH:
    2665            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2666
    2667        return path

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

    2669    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2670        if isinstance(expression, exp.JSONPathPart):
    2671            transform = self.TRANSFORMS.get(expression.__class__)
    2672            if not callable(transform):
    2673                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2674                return ""
    2675
    2676            return transform(self, expression)
    2677
    2678        if isinstance(expression, int):
    2679            return str(expression)
    2680
    2681        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2682            escaped = expression.replace("'", "\\'")
    2683            escaped = f"\\'{expression}\\'"
    2684        else:
    2685            escaped = expression.replace('"', '\\"')
    2686            escaped = f'"{escaped}"'
    2687
    2688        return escaped

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

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

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

    2693    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2694        null_handling = expression.args.get("null_handling")
    2695        null_handling = f" {null_handling}" if null_handling else ""
    2696
    2697        unique_keys = expression.args.get("unique_keys")
    2698        if unique_keys is not None:
    2699            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2700        else:
    2701            unique_keys = ""
    2702
    2703        return_type = self.sql(expression, "return_type")
    2704        return_type = f" RETURNING {return_type}" if return_type else ""
    2705        encoding = self.sql(expression, "encoding")
    2706        encoding = f" ENCODING {encoding}" if encoding else ""
    2707
    2708        return self.func(
    2709            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2710            *expression.expressions,
    2711            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2712        )

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

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

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

    2717    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2718        null_handling = expression.args.get("null_handling")
    2719        null_handling = f" {null_handling}" if null_handling else ""
    2720        return_type = self.sql(expression, "return_type")
    2721        return_type = f" RETURNING {return_type}" if return_type else ""
    2722        strict = " STRICT" if expression.args.get("strict") else ""
    2723        return self.func(
    2724            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2725        )

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

    2727    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2728        this = self.sql(expression, "this")
    2729        order = self.sql(expression, "order")
    2730        null_handling = expression.args.get("null_handling")
    2731        null_handling = f" {null_handling}" if null_handling else ""
    2732        return_type = self.sql(expression, "return_type")
    2733        return_type = f" RETURNING {return_type}" if return_type else ""
    2734        strict = " STRICT" if expression.args.get("strict") else ""
    2735        return self.func(
    2736            "JSON_ARRAYAGG",
    2737            this,
    2738            suffix=f"{order}{null_handling}{return_type}{strict})",
    2739        )

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

    2741    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2742        path = self.sql(expression, "path")
    2743        path = f" PATH {path}" if path else ""
    2744        nested_schema = self.sql(expression, "nested_schema")
    2745
    2746        if nested_schema:
    2747            return f"NESTED{path} {nested_schema}"
    2748
    2749        this = self.sql(expression, "this")
    2750        kind = self.sql(expression, "kind")
    2751        kind = f" {kind}" if kind else ""
    2752        return f"{this}{kind}{path}"

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

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

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

    2757    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2758        this = self.sql(expression, "this")
    2759        path = self.sql(expression, "path")
    2760        path = f", {path}" if path else ""
    2761        error_handling = expression.args.get("error_handling")
    2762        error_handling = f" {error_handling}" if error_handling else ""
    2763        empty_handling = expression.args.get("empty_handling")
    2764        empty_handling = f" {empty_handling}" if empty_handling else ""
    2765        schema = self.sql(expression, "schema")
    2766        return self.func(
    2767            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2768        )

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

    2770    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2771        this = self.sql(expression, "this")
    2772        kind = self.sql(expression, "kind")
    2773        path = self.sql(expression, "path")
    2774        path = f" {path}" if path else ""
    2775        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2776        return f"{this} {kind}{path}{as_json}"

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

    2778    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2779        this = self.sql(expression, "this")
    2780        path = self.sql(expression, "path")
    2781        path = f", {path}" if path else ""
    2782        expressions = self.expressions(expression)
    2783        with_ = (
    2784            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2785            if expressions
    2786            else ""
    2787        )
    2788        return f"OPENJSON({this}{path}){with_}"

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

    2790    def in_sql(self, expression: exp.In) -> str:
    2791        query = expression.args.get("query")
    2792        unnest = expression.args.get("unnest")
    2793        field = expression.args.get("field")
    2794        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2795
    2796        if query:
    2797            in_sql = self.sql(query)
    2798        elif unnest:
    2799            in_sql = self.in_unnest_op(unnest)
    2800        elif field:
    2801            in_sql = self.sql(field)
    2802        else:
    2803            in_sql = f"({self.expressions(expression, flat=True)})"
    2804
    2805        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2810    def interval_sql(self, expression: exp.Interval) -> str:
    2811        unit = self.sql(expression, "unit")
    2812        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2813            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    2814        unit = f" {unit}" if unit else ""
    2815
    2816        if self.SINGLE_STRING_INTERVAL:
    2817            this = expression.this.name if expression.this else ""
    2818            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2819
    2820        this = self.sql(expression, "this")
    2821        if this:
    2822            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2823            this = f" {this}" if unwrapped else f" ({this})"
    2824
    2825        return f"INTERVAL{this}{unit}"

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

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

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

    2830    def reference_sql(self, expression: exp.Reference) -> str:
    2831        this = self.sql(expression, "this")
    2832        expressions = self.expressions(expression, flat=True)
    2833        expressions = f"({expressions})" if expressions else ""
    2834        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2835        options = f" {options}" if options else ""
    2836        return f"REFERENCES {this}{expressions}{options}"

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

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

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

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

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

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

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

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

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

    2854    def alias_sql(self, expression: exp.Alias) -> str:
    2855        alias = self.sql(expression, "alias")
    2856        alias = f" AS {alias}" if alias else ""
    2857        return f"{self.sql(expression, 'this')}{alias}"

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

    2859    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    2860        alias = expression.args["alias"]
    2861        identifier_alias = isinstance(alias, exp.Identifier)
    2862
    2863        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2864            alias.replace(exp.Literal.string(alias.output_name))
    2865        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2866            alias.replace(exp.to_identifier(alias.output_name))
    2867
    2868        return self.alias_sql(expression)

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

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

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

    2873    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    2874        this = self.sql(expression, "this")
    2875        index = self.sql(expression, "expression")
    2876        return f"{this} AT {index}"

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

    2878    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2879        this = self.sql(expression, "this")
    2880        zone = self.sql(expression, "zone")
    2881        return f"{this} AT TIME ZONE {zone}"

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

    2883    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    2884        this = self.sql(expression, "this")
    2885        zone = self.sql(expression, "zone")
    2886        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

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

    2891    def and_sql(
    2892        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    2893    ) -> str:
    2894        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

    2896    def or_sql(
    2897        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    2898    ) -> str:
    2899        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

    2901    def xor_sql(
    2902        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    2903    ) -> str:
    2904        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

    2906    def connector_sql(
    2907        self,
    2908        expression: exp.Connector,
    2909        op: str,
    2910        stack: t.Optional[t.List[str | exp.Expression]] = None,
    2911    ) -> str:
    2912        if stack is not None:
    2913            if expression.expressions:
    2914                stack.append(self.expressions(expression, sep=f" {op} "))
    2915            else:
    2916                stack.append(expression.right)
    2917                if expression.comments and self.comments:
    2918                    for comment in expression.comments:
    2919                        if comment:
    2920                            op += f" /*{self.pad_comment(comment)}*/"
    2921                stack.extend((op, expression.left))
    2922            return op
    2923
    2924        stack = [expression]
    2925        sqls: t.List[str] = []
    2926        ops = set()
    2927
    2928        while stack:
    2929            node = stack.pop()
    2930            if isinstance(node, exp.Connector):
    2931                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    2932            else:
    2933                sql = self.sql(node)
    2934                if sqls and sqls[-1] in ops:
    2935                    sqls[-1] += f" {sql}"
    2936                else:
    2937                    sqls.append(sql)
    2938
    2939        sep = "\n" if self.pretty and self.too_wide(sqls) else " "
    2940        return sep.join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2960    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2961        format_sql = self.sql(expression, "format")
    2962        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2963        to_sql = self.sql(expression, "to")
    2964        to_sql = f" {to_sql}" if to_sql else ""
    2965        action = self.sql(expression, "action")
    2966        action = f" {action}" if action else ""
    2967        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

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

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

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

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

    2976    def collate_sql(self, expression: exp.Collate) -> str:
    2977        if self.COLLATE_IS_FUNC:
    2978            return self.function_fallback_sql(expression)
    2979        return self.binary(expression, "COLLATE")

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

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

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

    2984    def comment_sql(self, expression: exp.Comment) -> str:
    2985        this = self.sql(expression, "this")
    2986        kind = expression.args["kind"]
    2987        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    2988        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2989        expression_sql = self.sql(expression, "expression")
    2990        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

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

    2992    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2993        this = self.sql(expression, "this")
    2994        delete = " DELETE" if expression.args.get("delete") else ""
    2995        recompress = self.sql(expression, "recompress")
    2996        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2997        to_disk = self.sql(expression, "to_disk")
    2998        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2999        to_volume = self.sql(expression, "to_volume")
    3000        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3001        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    3003    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3004        where = self.sql(expression, "where")
    3005        group = self.sql(expression, "group")
    3006        aggregates = self.expressions(expression, key="aggregates")
    3007        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3008
    3009        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3010            return f"TTL {self.expressions(expression, flat=True)}"
    3011
    3012        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    3017    def commit_sql(self, expression: exp.Commit) -> str:
    3018        chain = expression.args.get("chain")
    3019        if chain is not None:
    3020            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3021
    3022        return f"COMMIT{chain or ''}"

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

    3024    def rollback_sql(self, expression: exp.Rollback) -> str:
    3025        savepoint = expression.args.get("savepoint")
    3026        savepoint = f" TO {savepoint}" if savepoint else ""
    3027        return f"ROLLBACK{savepoint}"

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

    3029    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3030        this = self.sql(expression, "this")
    3031
    3032        dtype = self.sql(expression, "dtype")
    3033        if dtype:
    3034            collate = self.sql(expression, "collate")
    3035            collate = f" COLLATE {collate}" if collate else ""
    3036            using = self.sql(expression, "using")
    3037            using = f" USING {using}" if using else ""
    3038            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    3039
    3040        default = self.sql(expression, "default")
    3041        if default:
    3042            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3043
    3044        comment = self.sql(expression, "comment")
    3045        if comment:
    3046            return f"ALTER COLUMN {this} COMMENT {comment}"
    3047
    3048        allow_null = expression.args.get("allow_null")
    3049        drop = expression.args.get("drop")
    3050
    3051        if not drop and not allow_null:
    3052            self.unsupported("Unsupported ALTER COLUMN syntax")
    3053
    3054        if allow_null is not None:
    3055            keyword = "DROP" if drop else "SET"
    3056            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3057
    3058        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    3060    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3061        this = self.sql(expression, "this")
    3062        if not isinstance(expression.this, exp.Var):
    3063            this = f"KEY DISTKEY {this}"
    3064        return f"ALTER DISTSTYLE {this}"

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

    3066    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3067        compound = " COMPOUND" if expression.args.get("compound") else ""
    3068        this = self.sql(expression, "this")
    3069        expressions = self.expressions(expression, flat=True)
    3070        expressions = f"({expressions})" if expressions else ""
    3071        return f"ALTER{compound} SORTKEY {this or expressions}"

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

    3073    def renametable_sql(self, expression: exp.RenameTable) -> str:
    3074        if not self.RENAME_TABLE_WITH_DB:
    3075            # Remove db from tables
    3076            expression = expression.transform(
    3077                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3078            ).assert_is(exp.RenameTable)
    3079        this = self.sql(expression, "this")
    3080        return f"RENAME TO {this}"

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

    3082    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3083        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3084        old_column = self.sql(expression, "this")
    3085        new_column = self.sql(expression, "to")
    3086        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

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

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

    3092    def altertable_sql(self, expression: exp.AlterTable) -> str:
    3093        actions = expression.args["actions"]
    3094
    3095        if isinstance(actions[0], exp.ColumnDef):
    3096            actions = self.add_column_sql(expression)
    3097        elif isinstance(actions[0], exp.Schema):
    3098            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3099        elif isinstance(actions[0], exp.Delete):
    3100            actions = self.expressions(expression, key="actions", flat=True)
    3101        else:
    3102            actions = self.expressions(expression, key="actions", flat=True)
    3103
    3104        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3105        on_cluster = self.sql(expression, "cluster")
    3106        on_cluster = f" {on_cluster}" if on_cluster else ""
    3107        only = " ONLY" if expression.args.get("only") else ""
    3108        options = self.expressions(expression, key="options")
    3109        options = f", {options}" if options else ""
    3110        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

    3112    def add_column_sql(self, expression: exp.AlterTable) -> str:
    3113        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3114            return self.expressions(
    3115                expression,
    3116                key="actions",
    3117                prefix="ADD COLUMN ",
    3118            )
    3119        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    3121    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3122        expressions = self.expressions(expression)
    3123        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3124        return f"DROP{exists}{expressions}"

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

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

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

    3129    def distinct_sql(self, expression: exp.Distinct) -> str:
    3130        this = self.expressions(expression, flat=True)
    3131
    3132        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3133            case = exp.case()
    3134            for arg in expression.expressions:
    3135                case = case.when(arg.is_(exp.null()), exp.null())
    3136            this = self.sql(case.else_(f"({this})"))
    3137
    3138        this = f" {this}" if this else ""
    3139
    3140        on = self.sql(expression, "on")
    3141        on = f" ON {on}" if on else ""
    3142        return f"DISTINCT{this}{on}"

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

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

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

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

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

    3150    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3151        this_sql = self.sql(expression, "this")
    3152        expression_sql = self.sql(expression, "expression")
    3153        kind = "MAX" if expression.args.get("max") else "MIN"
    3154        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3156    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3157        return self.sql(
    3158            exp.Cast(
    3159                this=exp.Div(this=expression.this, expression=expression.expression),
    3160                to=exp.DataType(this=exp.DataType.Type.INT),
    3161            )
    3162        )

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

    3164    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3165        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3166            return self.func(
    3167                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3168            )
    3169        return self.binary(expression, "||")

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

    3171    def div_sql(self, expression: exp.Div) -> str:
    3172        l, r = expression.left, expression.right
    3173
    3174        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3175            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3176
    3177        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3178            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3179                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3180
    3181        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3182            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3183                return self.sql(
    3184                    exp.cast(
    3185                        l / r,
    3186                        to=exp.DataType.Type.BIGINT,
    3187                    )
    3188                )
    3189
    3190        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3225    def is_sql(self, expression: exp.Is) -> str:
    3226        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3227            return self.sql(
    3228                expression.this if expression.expression.this else exp.not_(expression.this)
    3229            )
    3230        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3271    def try_sql(self, expression: exp.Try) -> str:
    3272        if not self.TRY_SUPPORTED:
    3273            self.unsupported("Unsupported TRY function")
    3274            return self.sql(expression, "this")
    3275
    3276        return self.func("TRY", expression.this)

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

    3278    def log_sql(self, expression: exp.Log) -> str:
    3279        this = expression.this
    3280        expr = expression.expression
    3281
    3282        if self.dialect.LOG_BASE_FIRST is False:
    3283            this, expr = expr, this
    3284        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3285            if this.name in ("2", "10"):
    3286                return self.func(f"LOG{this.name}", expr)
    3287
    3288            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3289
    3290        return self.func("LOG", this, expr)

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

    3292    def use_sql(self, expression: exp.Use) -> str:
    3293        kind = self.sql(expression, "kind")
    3294        kind = f" {kind}" if kind else ""
    3295        this = self.sql(expression, "this")
    3296        this = f" {this}" if this else ""
    3297        return f"USE{kind}{this}"

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

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

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

    3303    def function_fallback_sql(self, expression: exp.Func) -> str:
    3304        args = []
    3305
    3306        for key in expression.arg_types:
    3307            arg_value = expression.args.get(key)
    3308
    3309            if isinstance(arg_value, list):
    3310                for value in arg_value:
    3311                    args.append(value)
    3312            elif arg_value is not None:
    3313                args.append(arg_value)
    3314
    3315        if self.normalize_functions:
    3316            name = expression.sql_name()
    3317        else:
    3318            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3319
    3320        return self.func(name, *args)

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

    3322    def func(
    3323        self,
    3324        name: str,
    3325        *args: t.Optional[exp.Expression | str],
    3326        prefix: str = "(",
    3327        suffix: str = ")",
    3328    ) -> str:
    3329        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

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

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

    3337    def too_wide(self, args: t.Iterable) -> bool:
    3338        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

    3340    def format_time(
    3341        self,
    3342        expression: exp.Expression,
    3343        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3344        inverse_time_trie: t.Optional[t.Dict] = None,
    3345    ) -> t.Optional[str]:
    3346        return format_time(
    3347            self.sql(expression, "format"),
    3348            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3349            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3350        )

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

    3352    def expressions(
    3353        self,
    3354        expression: t.Optional[exp.Expression] = None,
    3355        key: t.Optional[str] = None,
    3356        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3357        flat: bool = False,
    3358        indent: bool = True,
    3359        skip_first: bool = False,
    3360        skip_last: bool = False,
    3361        sep: str = ", ",
    3362        prefix: str = "",
    3363        dynamic: bool = False,
    3364        new_line: bool = False,
    3365    ) -> str:
    3366        expressions = expression.args.get(key or "expressions") if expression else sqls
    3367
    3368        if not expressions:
    3369            return ""
    3370
    3371        if flat:
    3372            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3373
    3374        num_sqls = len(expressions)
    3375
    3376        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    3377        if self.pretty and not self.leading_comma:
    3378            stripped_sep = sep.strip()
    3379
    3380        result_sqls = []
    3381        for i, e in enumerate(expressions):
    3382            sql = self.sql(e, comment=False)
    3383            if not sql:
    3384                continue
    3385
    3386            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3387
    3388            if self.pretty:
    3389                if self.leading_comma:
    3390                    result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}")
    3391                else:
    3392                    result_sqls.append(
    3393                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    3394                    )
    3395            else:
    3396                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3397
    3398        if self.pretty and (not dynamic or self.too_wide(result_sqls)):
    3399            if new_line:
    3400                result_sqls.insert(0, "")
    3401                result_sqls.append("")
    3402            result_sql = "\n".join(result_sqls)
    3403        else:
    3404            result_sql = "".join(result_sqls)
    3405        return (
    3406            self.indent(result_sql, skip_first=skip_first, skip_last=skip_last)
    3407            if indent
    3408            else result_sql
    3409        )

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

    3411    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3412        flat = flat or isinstance(expression.parent, exp.Properties)
    3413        expressions_sql = self.expressions(expression, flat=flat)
    3414        if flat:
    3415            return f"{op} {expressions_sql}"
    3416        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3418    def naked_property(self, expression: exp.Property) -> str:
    3419        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3420        if not property_name:
    3421            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3422        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3430    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3431        this = self.sql(expression, "this")
    3432        expressions = self.no_identify(self.expressions, expression)
    3433        expressions = (
    3434            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3435        )
    3436        return f"{this}{expressions}"

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

    3438    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3439        this = self.sql(expression, "this")
    3440        expressions = self.expressions(expression, flat=True)
    3441        return f"{this}({expressions})"

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

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

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

    3446    def when_sql(self, expression: exp.When) -> str:
    3447        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3448        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3449        condition = self.sql(expression, "condition")
    3450        condition = f" AND {condition}" if condition else ""
    3451
    3452        then_expression = expression.args.get("then")
    3453        if isinstance(then_expression, exp.Insert):
    3454            this = self.sql(then_expression, "this")
    3455            this = f"INSERT {this}" if this else "INSERT"
    3456            then = self.sql(then_expression, "expression")
    3457            then = f"{this} VALUES {then}" if then else this
    3458        elif isinstance(then_expression, exp.Update):
    3459            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3460                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3461            else:
    3462                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3463        else:
    3464            then = self.sql(then_expression)
    3465        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3467    def merge_sql(self, expression: exp.Merge) -> str:
    3468        table = expression.this
    3469        table_alias = ""
    3470
    3471        hints = table.args.get("hints")
    3472        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3473            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3474            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3475
    3476        this = self.sql(table)
    3477        using = f"USING {self.sql(expression, 'using')}"
    3478        on = f"ON {self.sql(expression, 'on')}"
    3479        expressions = self.expressions(expression, sep=" ", indent=False)
    3480        sep = self.sep()
    3481
    3482        return self.prepend_ctes(
    3483            expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}"
    3484        )

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

    3486    def tochar_sql(self, expression: exp.ToChar) -> str:
    3487        if expression.args.get("format"):
    3488            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3489
    3490        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

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

    3492    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3493        if not self.SUPPORTS_TO_NUMBER:
    3494            self.unsupported("Unsupported TO_NUMBER function")
    3495            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3496
    3497        fmt = expression.args.get("format")
    3498        if not fmt:
    3499            self.unsupported("Conversion format is required for TO_NUMBER")
    3500            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3501
    3502        return self.func("TO_NUMBER", expression.this, fmt)

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

    3504    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3505        this = self.sql(expression, "this")
    3506        kind = self.sql(expression, "kind")
    3507        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3508        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3509        return f"{this}({kind}{args})"

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

    3511    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3512        this = self.sql(expression, "this")
    3513        max = self.sql(expression, "max")
    3514        min = self.sql(expression, "min")
    3515        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3523    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3524        expressions = self.expressions(expression, key="expressions", flat=True)
    3525        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3526        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3527        buckets = self.sql(expression, "buckets")
    3528        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3530    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3531        this = self.sql(expression, "this")
    3532        having = self.sql(expression, "having")
    3533
    3534        if having:
    3535            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3536
    3537        return self.func("ANY_VALUE", this)

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

    3539    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3540        transform = self.func("TRANSFORM", *expression.expressions)
    3541        row_format_before = self.sql(expression, "row_format_before")
    3542        row_format_before = f" {row_format_before}" if row_format_before else ""
    3543        record_writer = self.sql(expression, "record_writer")
    3544        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3545        using = f" USING {self.sql(expression, 'command_script')}"
    3546        schema = self.sql(expression, "schema")
    3547        schema = f" AS {schema}" if schema else ""
    3548        row_format_after = self.sql(expression, "row_format_after")
    3549        row_format_after = f" {row_format_after}" if row_format_after else ""
    3550        record_reader = self.sql(expression, "record_reader")
    3551        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3552        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

    3554    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3555        key_block_size = self.sql(expression, "key_block_size")
    3556        if key_block_size:
    3557            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3558
    3559        using = self.sql(expression, "using")
    3560        if using:
    3561            return f"USING {using}"
    3562
    3563        parser = self.sql(expression, "parser")
    3564        if parser:
    3565            return f"WITH PARSER {parser}"
    3566
    3567        comment = self.sql(expression, "comment")
    3568        if comment:
    3569            return f"COMMENT {comment}"
    3570
    3571        visible = expression.args.get("visible")
    3572        if visible is not None:
    3573            return "VISIBLE" if visible else "INVISIBLE"
    3574
    3575        engine_attr = self.sql(expression, "engine_attr")
    3576        if engine_attr:
    3577            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3578
    3579        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3580        if secondary_engine_attr:
    3581            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3582
    3583        self.unsupported("Unsupported index constraint option.")
    3584        return ""

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

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

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

    3590    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3591        kind = self.sql(expression, "kind")
    3592        kind = f"{kind} INDEX" if kind else "INDEX"
    3593        this = self.sql(expression, "this")
    3594        this = f" {this}" if this else ""
    3595        index_type = self.sql(expression, "index_type")
    3596        index_type = f" USING {index_type}" if index_type else ""
    3597        expressions = self.expressions(expression, flat=True)
    3598        expressions = f" ({expressions})" if expressions else ""
    3599        options = self.expressions(expression, key="options", sep=" ")
    3600        options = f" {options}" if options else ""
    3601        return f"{kind}{this}{index_type}{expressions}{options}"

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

    3603    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3604        if self.NVL2_SUPPORTED:
    3605            return self.function_fallback_sql(expression)
    3606
    3607        case = exp.Case().when(
    3608            expression.this.is_(exp.null()).not_(copy=False),
    3609            expression.args["true"],
    3610            copy=False,
    3611        )
    3612        else_cond = expression.args.get("false")
    3613        if else_cond:
    3614            case.else_(else_cond, copy=False)
    3615
    3616        return self.sql(case)

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

    3618    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3619        this = self.sql(expression, "this")
    3620        expr = self.sql(expression, "expression")
    3621        iterator = self.sql(expression, "iterator")
    3622        condition = self.sql(expression, "condition")
    3623        condition = f" IF {condition}" if condition else ""
    3624        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3632    def predict_sql(self, expression: exp.Predict) -> str:
    3633        model = self.sql(expression, "this")
    3634        model = f"MODEL {model}"
    3635        table = self.sql(expression, "expression")
    3636        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3637        parameters = self.sql(expression, "params_struct")
    3638        return self.func("PREDICT", model, table, parameters or None)

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

    3640    def forin_sql(self, expression: exp.ForIn) -> str:
    3641        this = self.sql(expression, "this")
    3642        expression_sql = self.sql(expression, "expression")
    3643        return f"FOR {this} DO {expression_sql}"

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

    3645    def refresh_sql(self, expression: exp.Refresh) -> str:
    3646        this = self.sql(expression, "this")
    3647        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3648        return f"REFRESH {table}{this}"

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

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

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

    3653    def toarray_sql(self, expression: exp.ToArray) -> str:
    3654        arg = expression.this
    3655        if not arg.type:
    3656            from sqlglot.optimizer.annotate_types import annotate_types
    3657
    3658            arg = annotate_types(arg)
    3659
    3660        if arg.is_type(exp.DataType.Type.ARRAY):
    3661            return self.sql(arg)
    3662
    3663        cond_for_null = arg.is_(exp.null())
    3664        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

    3666    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3667        this = expression.this
    3668        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3669            return self.sql(this)
    3670
    3671        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

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

    3673    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3674        this = expression.this
    3675        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3676            return self.sql(this)
    3677
    3678        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP))

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

    3680    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3681        this = expression.this
    3682        time_format = self.format_time(expression)
    3683
    3684        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3685            return self.sql(
    3686                exp.cast(
    3687                    exp.StrToTime(this=this, format=expression.args["format"]),
    3688                    exp.DataType.Type.DATE,
    3689                )
    3690            )
    3691
    3692        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3693            return self.sql(this)
    3694
    3695        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

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

    3697    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3698        return self.sql(
    3699            exp.func(
    3700                "DATEDIFF",
    3701                expression.this,
    3702                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    3703                "day",
    3704            )
    3705        )

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

    3707    def lastday_sql(self, expression: exp.LastDay) -> str:
    3708        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3709            return self.function_fallback_sql(expression)
    3710
    3711        unit = expression.text("unit")
    3712        if unit and unit != "MONTH":
    3713            self.unsupported("Date parts are not supported in LAST_DAY.")
    3714
    3715        return self.func("LAST_DAY", expression.this)

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

    3717    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    3718        from sqlglot.dialects.dialect import unit_to_str
    3719
    3720        return self.func(
    3721            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    3722        )

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

    3724    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3725        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3726            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3727            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3728            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3729            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3730
    3731        from sqlglot.dialects import Dialect
    3732
    3733        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3734        if self.dialect.__class__ != Dialect:
    3735            self.unsupported("ARRAY_ANY is unsupported")
    3736
    3737        return self.function_fallback_sql(expression)

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

    3739    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3740        expression.set("is_end_exclusive", None)
    3741        return self.function_fallback_sql(expression)

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

    3743    def struct_sql(self, expression: exp.Struct) -> str:
    3744        expression.set(
    3745            "expressions",
    3746            [
    3747                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    3748                if isinstance(e, exp.PropertyEQ)
    3749                else e
    3750                for e in expression.expressions
    3751            ],
    3752        )
    3753
    3754        return self.function_fallback_sql(expression)

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

    3756    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3757        low = self.sql(expression, "this")
    3758        high = self.sql(expression, "expression")
    3759
    3760        return f"{low} TO {high}"

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

    3762    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3763        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3764        tables = f" {self.expressions(expression)}"
    3765
    3766        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3767
    3768        on_cluster = self.sql(expression, "cluster")
    3769        on_cluster = f" {on_cluster}" if on_cluster else ""
    3770
    3771        identity = self.sql(expression, "identity")
    3772        identity = f" {identity} IDENTITY" if identity else ""
    3773
    3774        option = self.sql(expression, "option")
    3775        option = f" {option}" if option else ""
    3776
    3777        partition = self.sql(expression, "partition")
    3778        partition = f" {partition}" if partition else ""
    3779
    3780        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

    3784    def convert_sql(self, expression: exp.Convert) -> str:
    3785        to = expression.this
    3786        value = expression.expression
    3787        style = expression.args.get("style")
    3788        safe = expression.args.get("safe")
    3789        strict = expression.args.get("strict")
    3790
    3791        if not to or not value:
    3792            return ""
    3793
    3794        # Retrieve length of datatype and override to default if not specified
    3795        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3796            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    3797
    3798        transformed: t.Optional[exp.Expression] = None
    3799        cast = exp.Cast if strict else exp.TryCast
    3800
    3801        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    3802        if isinstance(style, exp.Literal) and style.is_int:
    3803            from sqlglot.dialects.tsql import TSQL
    3804
    3805            style_value = style.name
    3806            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    3807            if not converted_style:
    3808                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    3809
    3810            fmt = exp.Literal.string(converted_style)
    3811
    3812            if to.this == exp.DataType.Type.DATE:
    3813                transformed = exp.StrToDate(this=value, format=fmt)
    3814            elif to.this == exp.DataType.Type.DATETIME:
    3815                transformed = exp.StrToTime(this=value, format=fmt)
    3816            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3817                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    3818            elif to.this == exp.DataType.Type.TEXT:
    3819                transformed = exp.TimeToStr(this=value, format=fmt)
    3820
    3821        if not transformed:
    3822            transformed = cast(this=value, to=to, safe=safe)
    3823
    3824        return self.sql(transformed)

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

    3880    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    3881        option = self.sql(expression, "this")
    3882
    3883        if expression.expressions:
    3884            upper = option.upper()
    3885
    3886            # Snowflake FILE_FORMAT options are separated by whitespace
    3887            sep = " " if upper == "FILE_FORMAT" else ", "
    3888
    3889            # Databricks copy/format options do not set their list of values with EQ
    3890            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    3891            values = self.expressions(expression, flat=True, sep=sep)
    3892            return f"{option}{op}({values})"
    3893
    3894        value = self.sql(expression, "expression")
    3895
    3896        if not value:
    3897            return option
    3898
    3899        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    3900
    3901        return f"{option}{op}{value}"

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

    3903    def credentials_sql(self, expression: exp.Credentials) -> str:
    3904        cred_expr = expression.args.get("credentials")
    3905        if isinstance(cred_expr, exp.Literal):
    3906            # Redshift case: CREDENTIALS <string>
    3907            credentials = self.sql(expression, "credentials")
    3908            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    3909        else:
    3910            # Snowflake case: CREDENTIALS = (...)
    3911            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    3912            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    3913
    3914        storage = self.sql(expression, "storage")
    3915        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    3916
    3917        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    3918        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    3919
    3920        iam_role = self.sql(expression, "iam_role")
    3921        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    3922
    3923        region = self.sql(expression, "region")
    3924        region = f" REGION {region}" if region else ""
    3925
    3926        return f"{credentials}{storage}{encryption}{iam_role}{region}"

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

    3928    def copy_sql(self, expression: exp.Copy) -> str:
    3929        this = self.sql(expression, "this")
    3930        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    3931
    3932        credentials = self.sql(expression, "credentials")
    3933        credentials = self.seg(credentials) if credentials else ""
    3934        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    3935        files = self.expressions(expression, key="files", flat=True)
    3936
    3937        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    3938        params = self.expressions(
    3939            expression,
    3940            key="params",
    3941            sep=sep,
    3942            new_line=True,
    3943            skip_last=True,
    3944            skip_first=True,
    3945            indent=self.COPY_PARAMS_ARE_WRAPPED,
    3946        )
    3947
    3948        if params:
    3949            if self.COPY_PARAMS_ARE_WRAPPED:
    3950                params = f" WITH ({params})"
    3951            elif not self.pretty:
    3952                params = f" {params}"
    3953
    3954        return f"COPY{this}{kind} {files}{credentials}{params}"

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

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

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

    3959    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    3960        on_sql = "ON" if expression.args.get("on") else "OFF"
    3961        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    3962        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    3963        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    3964        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    3965
    3966        if filter_col or retention_period:
    3967            on_sql = self.func("ON", filter_col, retention_period)
    3968
    3969        return f"DATA_DELETION={on_sql}"

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

    3971    def maskingpolicycolumnconstraint_sql(
    3972        self, expression: exp.MaskingPolicyColumnConstraint
    3973    ) -> str:
    3974        this = self.sql(expression, "this")
    3975        expressions = self.expressions(expression, flat=True)
    3976        expressions = f" USING ({expressions})" if expressions else ""
    3977        return f"MASKING POLICY {this}{expressions}"

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

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