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

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

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

    525    def __init__(
    526        self,
    527        pretty: t.Optional[bool] = None,
    528        identify: str | bool = False,
    529        normalize: bool = False,
    530        pad: int = 2,
    531        indent: int = 2,
    532        normalize_functions: t.Optional[str | bool] = None,
    533        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    534        max_unsupported: int = 3,
    535        leading_comma: bool = False,
    536        max_text_width: int = 80,
    537        comments: bool = True,
    538        dialect: DialectType = None,
    539    ):
    540        import sqlglot
    541        from sqlglot.dialects import Dialect
    542
    543        self.pretty = pretty if pretty is not None else sqlglot.pretty
    544        self.identify = identify
    545        self.normalize = normalize
    546        self.pad = pad
    547        self._indent = indent
    548        self.unsupported_level = unsupported_level
    549        self.max_unsupported = max_unsupported
    550        self.leading_comma = leading_comma
    551        self.max_text_width = max_text_width
    552        self.comments = comments
    553        self.dialect = Dialect.get_or_raise(dialect)
    554
    555        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    556        self.normalize_functions = (
    557            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    558        )
    559
    560        self.unsupported_messages: t.List[str] = []
    561        self._escaped_quote_end: str = (
    562            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    563        )
    564        self._escaped_identifier_end: str = (
    565            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
    566        )

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
COLUMN_JOIN_MARKS_SUPPORTED = False
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = 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'}
STAR_MAPPING = {'except': 'EXCEPT', 'replace': 'REPLACE'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
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.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.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Union'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <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

    568    def generate(self, expression: exp.Expression, copy: bool = True) -> str:
    569        """
    570        Generates the SQL string corresponding to the given syntax tree.
    571
    572        Args:
    573            expression: The syntax tree.
    574            copy: Whether to copy the expression. The generator performs mutations so
    575                it is safer to copy.
    576
    577        Returns:
    578            The SQL string corresponding to `expression`.
    579        """
    580        if copy:
    581            expression = expression.copy()
    582
    583        expression = self.preprocess(expression)
    584
    585        self.unsupported_messages = []
    586        sql = self.sql(expression).strip()
    587
    588        if self.pretty:
    589            sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n")
    590
    591        if self.unsupported_level == ErrorLevel.IGNORE:
    592            return sql
    593
    594        if self.unsupported_level == ErrorLevel.WARN:
    595            for msg in self.unsupported_messages:
    596                logger.warning(msg)
    597        elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages:
    598            raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported))
    599
    600        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

    602    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    603        """Apply generic preprocessing transformations to a given expression."""
    604        if (
    605            not expression.parent
    606            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    607            and any(node.parent is not expression for node in expression.find_all(exp.With))
    608        ):
    609            from sqlglot.transforms import move_ctes_to_top_level
    610
    611            expression = move_ctes_to_top_level(expression)
    612
    613        if self.ENSURE_BOOLS:
    614            from sqlglot.transforms import ensure_bools
    615
    616            expression = ensure_bools(expression)
    617
    618        return expression

Apply generic preprocessing transformations to a given expression.

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

    620    def unsupported(self, message: str) -> None:
    621        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    622            raise UnsupportedError(message)
    623        self.unsupported_messages.append(message)

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

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

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

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

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

    631    def pad_comment(self, comment: str) -> str:
    632        comment = " " + comment if comment[0].strip() else comment
    633        comment = comment + " " if comment[-1].strip() else comment
    634        return comment

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

    636    def maybe_comment(
    637        self,
    638        sql: str,
    639        expression: t.Optional[exp.Expression] = None,
    640        comments: t.Optional[t.List[str]] = None,
    641    ) -> str:
    642        comments = (
    643            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    644            if self.comments
    645            else None
    646        )
    647
    648        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    649            return sql
    650
    651        comments_sql = " ".join(
    652            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    653        )
    654
    655        if not comments_sql:
    656            return sql
    657
    658        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    659            return (
    660                f"{self.sep()}{comments_sql}{sql}"
    661                if not sql or sql[0].isspace()
    662                else f"{comments_sql}{self.sep()}{sql}"
    663            )
    664
    665        return f"{sql} {comments_sql}"

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

    667    def wrap(self, expression: exp.Expression | str) -> str:
    668        this_sql = self.indent(
    669            (
    670                self.sql(expression)
    671                if isinstance(expression, exp.UNWRAPPED_QUERIES)
    672                else self.sql(expression, "this")
    673            ),
    674            level=1,
    675            pad=0,
    676        )
    677        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    679    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    680        original = self.identify
    681        self.identify = False
    682        result = func(*args, **kwargs)
    683        self.identify = original
    684        return result

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

    686    def normalize_func(self, name: str) -> str:
    687        if self.normalize_functions == "upper" or self.normalize_functions is True:
    688            return name.upper()
    689        if self.normalize_functions == "lower":
    690            return name.lower()
    691        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

    693    def indent(
    694        self,
    695        sql: str,
    696        level: int = 0,
    697        pad: t.Optional[int] = None,
    698        skip_first: bool = False,
    699        skip_last: bool = False,
    700    ) -> str:
    701        if not self.pretty:
    702            return sql
    703
    704        pad = self.pad if pad is None else pad
    705        lines = sql.split("\n")
    706
    707        return "\n".join(
    708            (
    709                line
    710                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    711                else f"{' ' * (level * self._indent + pad)}{line}"
    712            )
    713            for i, line in enumerate(lines)
    714        )

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

    716    def sql(
    717        self,
    718        expression: t.Optional[str | exp.Expression],
    719        key: t.Optional[str] = None,
    720        comment: bool = True,
    721    ) -> str:
    722        if not expression:
    723            return ""
    724
    725        if isinstance(expression, str):
    726            return expression
    727
    728        if key:
    729            value = expression.args.get(key)
    730            if value:
    731                return self.sql(value)
    732            return ""
    733
    734        transform = self.TRANSFORMS.get(expression.__class__)
    735
    736        if callable(transform):
    737            sql = transform(self, expression)
    738        elif isinstance(expression, exp.Expression):
    739            exp_handler_name = f"{expression.key}_sql"
    740
    741            if hasattr(self, exp_handler_name):
    742                sql = getattr(self, exp_handler_name)(expression)
    743            elif isinstance(expression, exp.Func):
    744                sql = self.function_fallback_sql(expression)
    745            elif isinstance(expression, exp.Property):
    746                sql = self.property_sql(expression)
    747            else:
    748                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    749        else:
    750            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    751
    752        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

    759    def cache_sql(self, expression: exp.Cache) -> str:
    760        lazy = " LAZY" if expression.args.get("lazy") else ""
    761        table = self.sql(expression, "this")
    762        options = expression.args.get("options")
    763        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    764        sql = self.sql(expression, "expression")
    765        sql = f" AS{self.sep()}{sql}" if sql else ""
    766        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    767        return self.prepend_ctes(expression, sql)

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

    769    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    770        if isinstance(expression.parent, exp.Cast):
    771            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    772        default = "DEFAULT " if expression.args.get("default") else ""
    773        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    775    def column_sql(self, expression: exp.Column) -> str:
    776        join_mark = " (+)" if expression.args.get("join_mark") else ""
    777
    778        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
    779            join_mark = ""
    780            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    781
    782        column = ".".join(
    783            self.sql(part)
    784            for part in (
    785                expression.args.get("catalog"),
    786                expression.args.get("db"),
    787                expression.args.get("table"),
    788                expression.args.get("this"),
    789            )
    790            if part
    791        )
    792
    793        return f"{column}{join_mark}"

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

    795    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    796        this = self.sql(expression, "this")
    797        this = f" {this}" if this else ""
    798        position = self.sql(expression, "position")
    799        return f"{position}{this}"

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

    801    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    802        column = self.sql(expression, "this")
    803        kind = self.sql(expression, "kind")
    804        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    805        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    806        kind = f"{sep}{kind}" if kind else ""
    807        constraints = f" {constraints}" if constraints else ""
    808        position = self.sql(expression, "position")
    809        position = f" {position}" if position else ""
    810
    811        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    812            kind = ""
    813
    814        return f"{exists}{column}{kind}{constraints}{position}"

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

    816    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    817        this = self.sql(expression, "this")
    818        kind_sql = self.sql(expression, "kind").strip()
    819        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    821    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    822        this = self.sql(expression, "this")
    823        if expression.args.get("not_null"):
    824            persisted = " PERSISTED NOT NULL"
    825        elif expression.args.get("persisted"):
    826            persisted = " PERSISTED"
    827        else:
    828            persisted = ""
    829        return f"AS {this}{persisted}"

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

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

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

    834    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    835        if isinstance(expression.this, list):
    836            this = self.wrap(self.expressions(expression, key="this", flat=True))
    837        else:
    838            this = self.sql(expression, "this")
    839
    840        return f"COMPRESS {this}"

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

    842    def generatedasidentitycolumnconstraint_sql(
    843        self, expression: exp.GeneratedAsIdentityColumnConstraint
    844    ) -> str:
    845        this = ""
    846        if expression.this is not None:
    847            on_null = " ON NULL" if expression.args.get("on_null") else ""
    848            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    849
    850        start = expression.args.get("start")
    851        start = f"START WITH {start}" if start else ""
    852        increment = expression.args.get("increment")
    853        increment = f" INCREMENT BY {increment}" if increment else ""
    854        minvalue = expression.args.get("minvalue")
    855        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    856        maxvalue = expression.args.get("maxvalue")
    857        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    858        cycle = expression.args.get("cycle")
    859        cycle_sql = ""
    860
    861        if cycle is not None:
    862            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    863            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    864
    865        sequence_opts = ""
    866        if start or increment or cycle_sql:
    867            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    868            sequence_opts = f" ({sequence_opts.strip()})"
    869
    870        expr = self.sql(expression, "expression")
    871        expr = f"({expr})" if expr else "IDENTITY"
    872
    873        return f"GENERATED{this} AS {expr}{sequence_opts}"

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

    875    def generatedasrowcolumnconstraint_sql(
    876        self, expression: exp.GeneratedAsRowColumnConstraint
    877    ) -> str:
    878        start = "START" if expression.args.get("start") else "END"
    879        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    880        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    882    def periodforsystemtimeconstraint_sql(
    883        self, expression: exp.PeriodForSystemTimeConstraint
    884    ) -> str:
    885        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

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

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

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

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

    893    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    894        desc = expression.args.get("desc")
    895        if desc is not None:
    896            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    897        return "PRIMARY KEY"

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

    899    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    900        this = self.sql(expression, "this")
    901        this = f" {this}" if this else ""
    902        index_type = expression.args.get("index_type")
    903        index_type = f" USING {index_type}" if index_type else ""
    904        on_conflict = self.sql(expression, "on_conflict")
    905        on_conflict = f" {on_conflict}" if on_conflict else ""
    906        return f"UNIQUE{this}{index_type}{on_conflict}"

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

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

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

    911    def create_sql(self, expression: exp.Create) -> str:
    912        kind = self.sql(expression, "kind")
    913        properties = expression.args.get("properties")
    914        properties_locs = self.locate_properties(properties) if properties else defaultdict()
    915
    916        this = self.createable_sql(expression, properties_locs)
    917
    918        properties_sql = ""
    919        if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get(
    920            exp.Properties.Location.POST_WITH
    921        ):
    922            properties_sql = self.sql(
    923                exp.Properties(
    924                    expressions=[
    925                        *properties_locs[exp.Properties.Location.POST_SCHEMA],
    926                        *properties_locs[exp.Properties.Location.POST_WITH],
    927                    ]
    928                )
    929            )
    930
    931        begin = " BEGIN" if expression.args.get("begin") else ""
    932        end = " END" if expression.args.get("end") else ""
    933
    934        expression_sql = self.sql(expression, "expression")
    935        if expression_sql:
    936            expression_sql = f"{begin}{self.sep()}{expression_sql}{end}"
    937
    938            if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return):
    939                if properties_locs.get(exp.Properties.Location.POST_ALIAS):
    940                    postalias_props_sql = self.properties(
    941                        exp.Properties(
    942                            expressions=properties_locs[exp.Properties.Location.POST_ALIAS]
    943                        ),
    944                        wrapped=False,
    945                    )
    946                    expression_sql = f" AS {postalias_props_sql}{expression_sql}"
    947                else:
    948                    expression_sql = f" AS{expression_sql}"
    949
    950        postindex_props_sql = ""
    951        if properties_locs.get(exp.Properties.Location.POST_INDEX):
    952            postindex_props_sql = self.properties(
    953                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]),
    954                wrapped=False,
    955                prefix=" ",
    956            )
    957
    958        indexes = self.expressions(expression, key="indexes", indent=False, sep=" ")
    959        indexes = f" {indexes}" if indexes else ""
    960        index_sql = indexes + postindex_props_sql
    961
    962        replace = " OR REPLACE" if expression.args.get("replace") else ""
    963        unique = " UNIQUE" if expression.args.get("unique") else ""
    964
    965        postcreate_props_sql = ""
    966        if properties_locs.get(exp.Properties.Location.POST_CREATE):
    967            postcreate_props_sql = self.properties(
    968                exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]),
    969                sep=" ",
    970                prefix=" ",
    971                wrapped=False,
    972            )
    973
    974        modifiers = "".join((replace, unique, postcreate_props_sql))
    975
    976        postexpression_props_sql = ""
    977        if properties_locs.get(exp.Properties.Location.POST_EXPRESSION):
    978            postexpression_props_sql = self.properties(
    979                exp.Properties(
    980                    expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION]
    981                ),
    982                sep=" ",
    983                prefix=" ",
    984                wrapped=False,
    985            )
    986
    987        exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else ""
    988        no_schema_binding = (
    989            " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else ""
    990        )
    991
    992        clone = self.sql(expression, "clone")
    993        clone = f" {clone}" if clone else ""
    994
    995        expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}"
    996        return self.prepend_ctes(expression, expression_sql)

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

     998    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
     999        start = self.sql(expression, "start")
    1000        start = f"START WITH {start}" if start else ""
    1001        increment = self.sql(expression, "increment")
    1002        increment = f" INCREMENT BY {increment}" if increment else ""
    1003        minvalue = self.sql(expression, "minvalue")
    1004        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    1005        maxvalue = self.sql(expression, "maxvalue")
    1006        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    1007        owned = self.sql(expression, "owned")
    1008        owned = f" OWNED BY {owned}" if owned else ""
    1009
    1010        cache = expression.args.get("cache")
    1011        if cache is None:
    1012            cache_str = ""
    1013        elif cache is True:
    1014            cache_str = " CACHE"
    1015        else:
    1016            cache_str = f" CACHE {cache}"
    1017
    1018        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1019        options = f" {options}" if options else ""
    1020
    1021        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

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

    1023    def clone_sql(self, expression: exp.Clone) -> str:
    1024        this = self.sql(expression, "this")
    1025        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1026        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1027        return f"{shallow}{keyword} {this}"

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

    1029    def describe_sql(self, expression: exp.Describe) -> str:
    1030        style = expression.args.get("style")
    1031        style = f" {style}" if style else ""
    1032        return f"DESCRIBE{style} {self.sql(expression, 'this')}"

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

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

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

    1038    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1039        with_ = self.sql(expression, "with")
    1040        if with_:
    1041            sql = f"{with_}{self.sep()}{sql}"
    1042        return sql

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

    1044    def with_sql(self, expression: exp.With) -> str:
    1045        sql = self.expressions(expression, flat=True)
    1046        recursive = (
    1047            "RECURSIVE "
    1048            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1049            else ""
    1050        )
    1051
    1052        return f"WITH {recursive}{sql}"

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

    1054    def cte_sql(self, expression: exp.CTE) -> str:
    1055        alias = self.sql(expression, "alias")
    1056
    1057        materialized = expression.args.get("materialized")
    1058        if materialized is False:
    1059            materialized = "NOT MATERIALIZED "
    1060        elif materialized:
    1061            materialized = "MATERIALIZED "
    1062
    1063        return f"{alias} AS {materialized or ''}{self.wrap(expression)}"

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

    1065    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1066        alias = self.sql(expression, "this")
    1067        columns = self.expressions(expression, key="columns", flat=True)
    1068        columns = f"({columns})" if columns else ""
    1069
    1070        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1071            columns = ""
    1072            self.unsupported("Named columns are not supported in table alias.")
    1073
    1074        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1075            alias = "_t"
    1076
    1077        return f"{alias}{columns}"

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

    1079    def bitstring_sql(self, expression: exp.BitString) -> str:
    1080        this = self.sql(expression, "this")
    1081        if self.dialect.BIT_START:
    1082            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1083        return f"{int(this, 2)}"

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

    1085    def hexstring_sql(self, expression: exp.HexString) -> str:
    1086        this = self.sql(expression, "this")
    1087        if self.dialect.HEX_START:
    1088            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1089        return f"{int(this, 16)}"

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

    1091    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1092        this = self.sql(expression, "this")
    1093        if self.dialect.BYTE_START:
    1094            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1095        return this

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

    1097    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1098        this = self.sql(expression, "this")
    1099        escape = expression.args.get("escape")
    1100
    1101        if self.dialect.UNICODE_START:
    1102            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
    1103            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
    1104
    1105        if escape:
    1106            pattern = re.compile(rf"{escape.name}(\d+)")
    1107        else:
    1108            pattern = ESCAPED_UNICODE_RE
    1109
    1110        this = pattern.sub(r"\\u\1", this)
    1111        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"

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

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

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

    1117    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1118        this = self.sql(expression, "this")
    1119        specifier = self.sql(expression, "expression")
    1120        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1121        return f"{this}{specifier}"

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

    1123    def datatype_sql(self, expression: exp.DataType) -> str:
    1124        type_value = expression.this
    1125
    1126        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1127            type_sql = self.sql(expression, "kind")
    1128        else:
    1129            type_sql = (
    1130                self.TYPE_MAPPING.get(type_value, type_value.value)
    1131                if isinstance(type_value, exp.DataType.Type)
    1132                else type_value
    1133            )
    1134
    1135        nested = ""
    1136        interior = self.expressions(expression, flat=True)
    1137        values = ""
    1138
    1139        if interior:
    1140            if expression.args.get("nested"):
    1141                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1142                if expression.args.get("values") is not None:
    1143                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1144                    values = self.expressions(expression, key="values", flat=True)
    1145                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1146            elif type_value == exp.DataType.Type.INTERVAL:
    1147                nested = f" {interior}"
    1148            else:
    1149                nested = f"({interior})"
    1150
    1151        type_sql = f"{type_sql}{nested}{values}"
    1152        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1153            exp.DataType.Type.TIMETZ,
    1154            exp.DataType.Type.TIMESTAMPTZ,
    1155        ):
    1156            type_sql = f"{type_sql} WITH TIME ZONE"
    1157
    1158        return type_sql

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

    1160    def directory_sql(self, expression: exp.Directory) -> str:
    1161        local = "LOCAL " if expression.args.get("local") else ""
    1162        row_format = self.sql(expression, "row_format")
    1163        row_format = f" {row_format}" if row_format else ""
    1164        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1166    def delete_sql(self, expression: exp.Delete) -> str:
    1167        this = self.sql(expression, "this")
    1168        this = f" FROM {this}" if this else ""
    1169        using = self.sql(expression, "using")
    1170        using = f" USING {using}" if using else ""
    1171        where = self.sql(expression, "where")
    1172        returning = self.sql(expression, "returning")
    1173        limit = self.sql(expression, "limit")
    1174        tables = self.expressions(expression, key="tables")
    1175        tables = f" {tables}" if tables else ""
    1176        if self.RETURNING_END:
    1177            expression_sql = f"{this}{using}{where}{returning}{limit}"
    1178        else:
    1179            expression_sql = f"{returning}{this}{using}{where}{limit}"
    1180        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1182    def drop_sql(self, expression: exp.Drop) -> str:
    1183        this = self.sql(expression, "this")
    1184        expressions = self.expressions(expression, flat=True)
    1185        expressions = f" ({expressions})" if expressions else ""
    1186        kind = expression.args["kind"]
    1187        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1188        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1189        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1190        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1191        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1192        purge = " PURGE" if expression.args.get("purge") else ""
    1193        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}"

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

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

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

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

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

    1201    def fetch_sql(self, expression: exp.Fetch) -> str:
    1202        direction = expression.args.get("direction")
    1203        direction = f" {direction}" if direction else ""
    1204        count = expression.args.get("count")
    1205        count = f" {count}" if count else ""
    1206        if expression.args.get("percent"):
    1207            count = f"{count} PERCENT"
    1208        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1209        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1211    def filter_sql(self, expression: exp.Filter) -> str:
    1212        if self.AGGREGATE_FILTER_SUPPORTED:
    1213            this = self.sql(expression, "this")
    1214            where = self.sql(expression, "expression").strip()
    1215            return f"{this} FILTER({where})"
    1216
    1217        agg = expression.this
    1218        agg_arg = agg.this
    1219        cond = expression.expression.this
    1220        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1221        return self.sql(agg)

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

    1223    def hint_sql(self, expression: exp.Hint) -> str:
    1224        if not self.QUERY_HINTS:
    1225            self.unsupported("Hints are not supported")
    1226            return ""
    1227
    1228        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1230    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1231        using = self.sql(expression, "using")
    1232        using = f" USING {using}" if using else ""
    1233        columns = self.expressions(expression, key="columns", flat=True)
    1234        columns = f"({columns})" if columns else ""
    1235        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1236        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1237        where = self.sql(expression, "where")
    1238        include = self.expressions(expression, key="include", flat=True)
    1239        if include:
    1240            include = f" INCLUDE ({include})"
    1241        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1242        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1243        tablespace = self.sql(expression, "tablespace")
    1244        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1245
    1246        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}"

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

    1248    def index_sql(self, expression: exp.Index) -> str:
    1249        unique = "UNIQUE " if expression.args.get("unique") else ""
    1250        primary = "PRIMARY " if expression.args.get("primary") else ""
    1251        amp = "AMP " if expression.args.get("amp") else ""
    1252        name = self.sql(expression, "this")
    1253        name = f"{name} " if name else ""
    1254        table = self.sql(expression, "table")
    1255        table = f"{self.INDEX_ON} {table}" if table else ""
    1256
    1257        index = "INDEX " if not table else ""
    1258
    1259        params = self.sql(expression, "params")
    1260        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1262    def identifier_sql(self, expression: exp.Identifier) -> str:
    1263        text = expression.name
    1264        lower = text.lower()
    1265        text = lower if self.normalize and not expression.quoted else text
    1266        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1267        if (
    1268            expression.quoted
    1269            or self.dialect.can_identify(text, self.identify)
    1270            or lower in self.RESERVED_KEYWORDS
    1271            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1272        ):
    1273            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1274        return text

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

    1276    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1277        input_format = self.sql(expression, "input_format")
    1278        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1279        output_format = self.sql(expression, "output_format")
    1280        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1281        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1290    def properties_sql(self, expression: exp.Properties) -> str:
    1291        root_properties = []
    1292        with_properties = []
    1293
    1294        for p in expression.expressions:
    1295            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1296            if p_loc == exp.Properties.Location.POST_WITH:
    1297                with_properties.append(p)
    1298            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1299                root_properties.append(p)
    1300
    1301        return self.root_properties(
    1302            exp.Properties(expressions=root_properties)
    1303        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    1305    def root_properties(self, properties: exp.Properties) -> str:
    1306        if properties.expressions:
    1307            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1308        return ""

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

    1310    def properties(
    1311        self,
    1312        properties: exp.Properties,
    1313        prefix: str = "",
    1314        sep: str = ", ",
    1315        suffix: str = "",
    1316        wrapped: bool = True,
    1317    ) -> str:
    1318        if properties.expressions:
    1319            expressions = self.expressions(properties, sep=sep, indent=False)
    1320            if expressions:
    1321                expressions = self.wrap(expressions) if wrapped else expressions
    1322                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1323        return ""

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

    1325    def with_properties(self, properties: exp.Properties) -> str:
    1326        return self.properties(properties, prefix=self.seg("WITH"))

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

    1328    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1329        properties_locs = defaultdict(list)
    1330        for p in properties.expressions:
    1331            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1332            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1333                properties_locs[p_loc].append(p)
    1334            else:
    1335                self.unsupported(f"Unsupported property {p.key}")
    1336
    1337        return properties_locs

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

    1339    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1340        if isinstance(expression.this, exp.Dot):
    1341            return self.sql(expression, "this")
    1342        return f"'{expression.name}'" if string_key else expression.name

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

    1344    def property_sql(self, expression: exp.Property) -> str:
    1345        property_cls = expression.__class__
    1346        if property_cls == exp.Property:
    1347            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1348
    1349        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1350        if not property_name:
    1351            self.unsupported(f"Unsupported property {expression.key}")
    1352
    1353        return f"{property_name}={self.sql(expression, 'this')}"

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

    1355    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1356        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1357            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1358            options = f" {options}" if options else ""
    1359
    1360            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1361            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1362                like = f"({like})"
    1363
    1364            return like
    1365
    1366        if expression.expressions:
    1367            self.unsupported("Transpilation of LIKE property options is unsupported")
    1368
    1369        select = exp.select("*").from_(expression.this).limit(0)
    1370        return f"AS {self.sql(select)}"

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

    1372    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1373        no = "NO " if expression.args.get("no") else ""
    1374        protection = " PROTECTION" if expression.args.get("protection") else ""
    1375        return f"{no}FALLBACK{protection}"

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

    1377    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1378        no = "NO " if expression.args.get("no") else ""
    1379        local = expression.args.get("local")
    1380        local = f"{local} " if local else ""
    1381        dual = "DUAL " if expression.args.get("dual") else ""
    1382        before = "BEFORE " if expression.args.get("before") else ""
    1383        after = "AFTER " if expression.args.get("after") else ""
    1384        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1386    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1387        freespace = self.sql(expression, "this")
    1388        percent = " PERCENT" if expression.args.get("percent") else ""
    1389        return f"FREESPACE={freespace}{percent}"

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

    1391    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1392        if expression.args.get("default"):
    1393            property = "DEFAULT"
    1394        elif expression.args.get("on"):
    1395            property = "ON"
    1396        else:
    1397            property = "OFF"
    1398        return f"CHECKSUM={property}"

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

    1400    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1401        if expression.args.get("no"):
    1402            return "NO MERGEBLOCKRATIO"
    1403        if expression.args.get("default"):
    1404            return "DEFAULT MERGEBLOCKRATIO"
    1405
    1406        percent = " PERCENT" if expression.args.get("percent") else ""
    1407        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1409    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1410        default = expression.args.get("default")
    1411        minimum = expression.args.get("minimum")
    1412        maximum = expression.args.get("maximum")
    1413        if default or minimum or maximum:
    1414            if default:
    1415                prop = "DEFAULT"
    1416            elif minimum:
    1417                prop = "MINIMUM"
    1418            else:
    1419                prop = "MAXIMUM"
    1420            return f"{prop} DATABLOCKSIZE"
    1421        units = expression.args.get("units")
    1422        units = f" {units}" if units else ""
    1423        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1425    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1426        autotemp = expression.args.get("autotemp")
    1427        always = expression.args.get("always")
    1428        default = expression.args.get("default")
    1429        manual = expression.args.get("manual")
    1430        never = expression.args.get("never")
    1431
    1432        if autotemp is not None:
    1433            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1434        elif always:
    1435            prop = "ALWAYS"
    1436        elif default:
    1437            prop = "DEFAULT"
    1438        elif manual:
    1439            prop = "MANUAL"
    1440        elif never:
    1441            prop = "NEVER"
    1442        return f"BLOCKCOMPRESSION={prop}"

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

    1444    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1445        no = expression.args.get("no")
    1446        no = " NO" if no else ""
    1447        concurrent = expression.args.get("concurrent")
    1448        concurrent = " CONCURRENT" if concurrent else ""
    1449        target = self.sql(expression, "target")
    1450        target = f" {target}" if target else ""
    1451        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1453    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1454        if isinstance(expression.this, list):
    1455            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1456        if expression.this:
    1457            modulus = self.sql(expression, "this")
    1458            remainder = self.sql(expression, "expression")
    1459            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1460
    1461        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1462        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1463        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1465    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1466        this = self.sql(expression, "this")
    1467
    1468        for_values_or_default = expression.expression
    1469        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1470            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1471        else:
    1472            for_values_or_default = " DEFAULT"
    1473
    1474        return f"PARTITION OF {this}{for_values_or_default}"

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

    1476    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1477        kind = expression.args.get("kind")
    1478        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1479        for_or_in = expression.args.get("for_or_in")
    1480        for_or_in = f" {for_or_in}" if for_or_in else ""
    1481        lock_type = expression.args.get("lock_type")
    1482        override = " OVERRIDE" if expression.args.get("override") else ""
    1483        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1485    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1486        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1487        statistics = expression.args.get("statistics")
    1488        statistics_sql = ""
    1489        if statistics is not None:
    1490            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1491        return f"{data_sql}{statistics_sql}"

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

    1493    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1494        sql = "WITH(SYSTEM_VERSIONING=ON"
    1495
    1496        if expression.this:
    1497            history_table = self.sql(expression, "this")
    1498            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1499
    1500            if expression.expression:
    1501                data_consistency_check = self.sql(expression, "expression")
    1502                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1503
    1504            sql = f"{sql})"
    1505
    1506        return f"{sql})"

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

    1508    def insert_sql(self, expression: exp.Insert) -> str:
    1509        hint = self.sql(expression, "hint")
    1510        overwrite = expression.args.get("overwrite")
    1511
    1512        if isinstance(expression.this, exp.Directory):
    1513            this = " OVERWRITE" if overwrite else " INTO"
    1514        else:
    1515            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1516
    1517        alternative = expression.args.get("alternative")
    1518        alternative = f" OR {alternative}" if alternative else ""
    1519        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1520        is_function = expression.args.get("is_function")
    1521        if is_function:
    1522            this = f"{this} FUNCTION"
    1523        this = f"{this} {self.sql(expression, 'this')}"
    1524
    1525        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1526        partition_sql = (
    1527            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1528        )
    1529        where = self.sql(expression, "where")
    1530        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1531        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1532        on_conflict = self.sql(expression, "conflict")
    1533        on_conflict = f" {on_conflict}" if on_conflict else ""
    1534        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1535        returning = self.sql(expression, "returning")
    1536
    1537        if self.RETURNING_END:
    1538            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1539        else:
    1540            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1541
    1542        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1543        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

    1554    def kill_sql(self, expression: exp.Kill) -> str:
    1555        kind = self.sql(expression, "kind")
    1556        kind = f" {kind}" if kind else ""
    1557        this = self.sql(expression, "this")
    1558        this = f" {this}" if this else ""
    1559        return f"KILL{kind}{this}"

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

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

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

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

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

    1567    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1568        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1569
    1570        constraint = self.sql(expression, "constraint")
    1571        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1572
    1573        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1574        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1575        action = self.sql(expression, "action")
    1576
    1577        expressions = self.expressions(expression, flat=True)
    1578        if expressions:
    1579            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1580            expressions = f" {set_keyword}{expressions}"
    1581
    1582        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"

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

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

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

    1587    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1588        fields = self.sql(expression, "fields")
    1589        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1590        escaped = self.sql(expression, "escaped")
    1591        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1592        items = self.sql(expression, "collection_items")
    1593        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1594        keys = self.sql(expression, "map_keys")
    1595        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1596        lines = self.sql(expression, "lines")
    1597        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1598        null = self.sql(expression, "null")
    1599        null = f" NULL DEFINED AS {null}" if null else ""
    1600        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1605    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1606        this = f"{self.sql(expression, 'this')} INDEX"
    1607        target = self.sql(expression, "target")
    1608        target = f" FOR {target}" if target else ""
    1609        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1611    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1612        this = self.sql(expression, "this")
    1613        kind = self.sql(expression, "kind")
    1614        expr = self.sql(expression, "expression")
    1615        return f"{this} ({kind} => {expr})"

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

    1617    def table_parts(self, expression: exp.Table) -> str:
    1618        return ".".join(
    1619            self.sql(part)
    1620            for part in (
    1621                expression.args.get("catalog"),
    1622                expression.args.get("db"),
    1623                expression.args.get("this"),
    1624            )
    1625            if part is not None
    1626        )

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

    1628    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1629        table = self.table_parts(expression)
    1630        only = "ONLY " if expression.args.get("only") else ""
    1631        version = self.sql(expression, "version")
    1632        version = f" {version}" if version else ""
    1633        alias = self.sql(expression, "alias")
    1634        alias = f"{sep}{alias}" if alias else ""
    1635        hints = self.expressions(expression, key="hints", sep=" ")
    1636        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1637        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1638        pivots = f" {pivots}" if pivots else ""
    1639        joins = self.indent(
    1640            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1641        )
    1642        laterals = self.expressions(expression, key="laterals", sep="")
    1643
    1644        file_format = self.sql(expression, "format")
    1645        if file_format:
    1646            pattern = self.sql(expression, "pattern")
    1647            pattern = f", PATTERN => {pattern}" if pattern else ""
    1648            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1649
    1650        ordinality = expression.args.get("ordinality") or ""
    1651        if ordinality:
    1652            ordinality = f" WITH ORDINALITY{alias}"
    1653            alias = ""
    1654
    1655        when = self.sql(expression, "when")
    1656        if when:
    1657            table = f"{table} {when}"
    1658
    1659        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"

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

    1661    def tablesample_sql(
    1662        self,
    1663        expression: exp.TableSample,
    1664        sep: str = " AS ",
    1665        tablesample_keyword: t.Optional[str] = None,
    1666    ) -> str:
    1667        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1668            table = expression.this.copy()
    1669            table.set("alias", None)
    1670            this = self.sql(table)
    1671            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1672        else:
    1673            this = self.sql(expression, "this")
    1674            alias = ""
    1675
    1676        method = self.sql(expression, "method")
    1677        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1678        numerator = self.sql(expression, "bucket_numerator")
    1679        denominator = self.sql(expression, "bucket_denominator")
    1680        field = self.sql(expression, "bucket_field")
    1681        field = f" ON {field}" if field else ""
    1682        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1683        seed = self.sql(expression, "seed")
    1684        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1685
    1686        size = self.sql(expression, "size")
    1687        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1688            size = f"{size} ROWS"
    1689
    1690        percent = self.sql(expression, "percent")
    1691        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1692            percent = f"{percent} PERCENT"
    1693
    1694        expr = f"{bucket}{percent}{size}"
    1695        if self.TABLESAMPLE_REQUIRES_PARENS:
    1696            expr = f"({expr})"
    1697
    1698        return (
    1699            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
    1700        )

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

    1702    def pivot_sql(self, expression: exp.Pivot) -> str:
    1703        expressions = self.expressions(expression, flat=True)
    1704
    1705        if expression.this:
    1706            this = self.sql(expression, "this")
    1707            if not expressions:
    1708                return f"UNPIVOT {this}"
    1709
    1710            on = f"{self.seg('ON')} {expressions}"
    1711            using = self.expressions(expression, key="using", flat=True)
    1712            using = f"{self.seg('USING')} {using}" if using else ""
    1713            group = self.sql(expression, "group")
    1714            return f"PIVOT {this}{on}{using}{group}"
    1715
    1716        alias = self.sql(expression, "alias")
    1717        alias = f" AS {alias}" if alias else ""
    1718        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    1719        field = self.sql(expression, "field")
    1720        include_nulls = expression.args.get("include_nulls")
    1721        if include_nulls is not None:
    1722            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1723        else:
    1724            nulls = ""
    1725        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1727    def version_sql(self, expression: exp.Version) -> str:
    1728        this = f"FOR {expression.name}"
    1729        kind = expression.text("kind")
    1730        expr = self.sql(expression, "expression")
    1731        return f"{this} {kind} {expr}"

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

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

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

    1736    def update_sql(self, expression: exp.Update) -> str:
    1737        this = self.sql(expression, "this")
    1738        set_sql = self.expressions(expression, flat=True)
    1739        from_sql = self.sql(expression, "from")
    1740        where_sql = self.sql(expression, "where")
    1741        returning = self.sql(expression, "returning")
    1742        order = self.sql(expression, "order")
    1743        limit = self.sql(expression, "limit")
    1744        if self.RETURNING_END:
    1745            expression_sql = f"{from_sql}{where_sql}{returning}"
    1746        else:
    1747            expression_sql = f"{returning}{from_sql}{where_sql}"
    1748        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1749        return self.prepend_ctes(expression, sql)

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

    1751    def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str:
    1752        values_as_table = values_as_table and self.VALUES_AS_TABLE
    1753
    1754        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1755        if values_as_table or not expression.find_ancestor(exp.From, exp.Join):
    1756            args = self.expressions(expression)
    1757            alias = self.sql(expression, "alias")
    1758            values = f"VALUES{self.seg('')}{args}"
    1759            values = (
    1760                f"({values})"
    1761                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1762                else values
    1763            )
    1764            return f"{values} AS {alias}" if alias else values
    1765
    1766        # Converts `VALUES...` expression into a series of select unions.
    1767        alias_node = expression.args.get("alias")
    1768        column_names = alias_node and alias_node.columns
    1769
    1770        selects: t.List[exp.Query] = []
    1771
    1772        for i, tup in enumerate(expression.expressions):
    1773            row = tup.expressions
    1774
    1775            if i == 0 and column_names:
    1776                row = [
    1777                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1778                ]
    1779
    1780            selects.append(exp.Select(expressions=row))
    1781
    1782        if self.pretty:
    1783            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1784            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1785            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1786            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1787            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    1788
    1789        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1790        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1791        return f"({unions}){alias}"

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

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

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

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

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

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

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

    1804    def group_sql(self, expression: exp.Group) -> str:
    1805        group_by = self.op_expressions("GROUP BY", expression)
    1806
    1807        if expression.args.get("all"):
    1808            return f"{group_by} ALL"
    1809
    1810        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1811        grouping_sets = (
    1812            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1813        )
    1814
    1815        cube = expression.args.get("cube", [])
    1816        if seq_get(cube, 0) is True:
    1817            return f"{group_by}{self.seg('WITH CUBE')}"
    1818        else:
    1819            cube_sql = self.expressions(expression, key="cube", indent=False)
    1820            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1821
    1822        rollup = expression.args.get("rollup", [])
    1823        if seq_get(rollup, 0) is True:
    1824            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1825        else:
    1826            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1827            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1828
    1829        groupings = csv(
    1830            grouping_sets,
    1831            cube_sql,
    1832            rollup_sql,
    1833            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1834            sep=self.GROUPINGS_SEP,
    1835        )
    1836
    1837        if expression.args.get("expressions") and groupings:
    1838            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1839
    1840        return f"{group_by}{groupings}"

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

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

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

    1846    def connect_sql(self, expression: exp.Connect) -> str:
    1847        start = self.sql(expression, "start")
    1848        start = self.seg(f"START WITH {start}") if start else ""
    1849        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    1850        connect = self.sql(expression, "connect")
    1851        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    1852        return start + connect

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

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

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

    1857    def join_sql(self, expression: exp.Join) -> str:
    1858        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1859            side = None
    1860        else:
    1861            side = expression.side
    1862
    1863        op_sql = " ".join(
    1864            op
    1865            for op in (
    1866                expression.method,
    1867                "GLOBAL" if expression.args.get("global") else None,
    1868                side,
    1869                expression.kind,
    1870                expression.hint if self.JOIN_HINTS else None,
    1871            )
    1872            if op
    1873        )
    1874        match_cond = self.sql(expression, "match_condition")
    1875        match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else ""
    1876        on_sql = self.sql(expression, "on")
    1877        using = expression.args.get("using")
    1878
    1879        if not on_sql and using:
    1880            on_sql = csv(*(self.sql(column) for column in using))
    1881
    1882        this = expression.this
    1883        this_sql = self.sql(this)
    1884
    1885        if on_sql:
    1886            on_sql = self.indent(on_sql, skip_first=True)
    1887            space = self.seg(" " * self.pad) if self.pretty else " "
    1888            if using:
    1889                on_sql = f"{space}USING ({on_sql})"
    1890            else:
    1891                on_sql = f"{space}ON {on_sql}"
    1892        elif not op_sql:
    1893            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    1894                return f" {this_sql}"
    1895
    1896            return f", {this_sql}"
    1897
    1898        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1899        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

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

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

    1906    def lateral_op(self, expression: exp.Lateral) -> str:
    1907        cross_apply = expression.args.get("cross_apply")
    1908
    1909        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    1910        if cross_apply is True:
    1911            op = "INNER JOIN "
    1912        elif cross_apply is False:
    1913            op = "LEFT JOIN "
    1914        else:
    1915            op = ""
    1916
    1917        return f"{op}LATERAL"

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

    1919    def lateral_sql(self, expression: exp.Lateral) -> str:
    1920        this = self.sql(expression, "this")
    1921
    1922        if expression.args.get("view"):
    1923            alias = expression.args["alias"]
    1924            columns = self.expressions(alias, key="columns", flat=True)
    1925            table = f" {alias.name}" if alias.name else ""
    1926            columns = f" AS {columns}" if columns else ""
    1927            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1928            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1929
    1930        alias = self.sql(expression, "alias")
    1931        alias = f" AS {alias}" if alias else ""
    1932        return f"{self.lateral_op(expression)} {this}{alias}"

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

    1934    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1935        this = self.sql(expression, "this")
    1936
    1937        args = [
    1938            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    1939            for e in (expression.args.get(k) for k in ("offset", "expression"))
    1940            if e
    1941        ]
    1942
    1943        args_sql = ", ".join(self.sql(e) for e in args)
    1944        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    1945        expressions = self.expressions(expression, flat=True)
    1946        expressions = f" BY {expressions}" if expressions else ""
    1947
    1948        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    1950    def offset_sql(self, expression: exp.Offset) -> str:
    1951        this = self.sql(expression, "this")
    1952        value = expression.expression
    1953        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    1954        expressions = self.expressions(expression, flat=True)
    1955        expressions = f" BY {expressions}" if expressions else ""
    1956        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    1958    def setitem_sql(self, expression: exp.SetItem) -> str:
    1959        kind = self.sql(expression, "kind")
    1960        kind = f"{kind} " if kind else ""
    1961        this = self.sql(expression, "this")
    1962        expressions = self.expressions(expression)
    1963        collate = self.sql(expression, "collate")
    1964        collate = f" COLLATE {collate}" if collate else ""
    1965        global_ = "GLOBAL " if expression.args.get("global") else ""
    1966        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1968    def set_sql(self, expression: exp.Set) -> str:
    1969        expressions = (
    1970            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1971        )
    1972        tag = " TAG" if expression.args.get("tag") else ""
    1973        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    1978    def lock_sql(self, expression: exp.Lock) -> str:
    1979        if not self.LOCKING_READS_SUPPORTED:
    1980            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1981            return ""
    1982
    1983        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1984        expressions = self.expressions(expression, flat=True)
    1985        expressions = f" OF {expressions}" if expressions else ""
    1986        wait = expression.args.get("wait")
    1987
    1988        if wait is not None:
    1989            if isinstance(wait, exp.Literal):
    1990                wait = f" WAIT {self.sql(wait)}"
    1991            else:
    1992                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1993
    1994        return f"{lock_type}{expressions}{wait or ''}"

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

    1996    def literal_sql(self, expression: exp.Literal) -> str:
    1997        text = expression.this or ""
    1998        if expression.is_string:
    1999            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2000        return text

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

    2002    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2003        if self.dialect.ESCAPED_SEQUENCES:
    2004            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2005            text = "".join(
    2006                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2007            )
    2008
    2009        if self.pretty:
    2010            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    2011
    2012        return text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)

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

    2014    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2015        local = " LOCAL" if expression.args.get("local") else ""
    2016        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2017        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2018        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2019        partition = self.sql(expression, "partition")
    2020        partition = f" {partition}" if partition else ""
    2021        input_format = self.sql(expression, "input_format")
    2022        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2023        serde = self.sql(expression, "serde")
    2024        serde = f" SERDE {serde}" if serde else ""
    2025        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2027    def null_sql(self, *_) -> str:
    2028        return "NULL"

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

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

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

    2033    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2034        this = self.sql(expression, "this")
    2035        this = f"{this} " if this else this
    2036        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2037        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2038        interpolated_values = [
    2039            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
    2040            for named_expression in expression.args.get("interpolate") or []
    2041        ]
    2042        interpolate = (
    2043            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2044        )
    2045        return f"{order}{interpolate}"

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

    2047    def withfill_sql(self, expression: exp.WithFill) -> str:
    2048        from_sql = self.sql(expression, "from")
    2049        from_sql = f" FROM {from_sql}" if from_sql else ""
    2050        to_sql = self.sql(expression, "to")
    2051        to_sql = f" TO {to_sql}" if to_sql else ""
    2052        step_sql = self.sql(expression, "step")
    2053        step_sql = f" STEP {step_sql}" if step_sql else ""
    2054        return f"WITH FILL{from_sql}{to_sql}{step_sql}"

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

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

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

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

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

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

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

    2065    def ordered_sql(self, expression: exp.Ordered) -> str:
    2066        desc = expression.args.get("desc")
    2067        asc = not desc
    2068
    2069        nulls_first = expression.args.get("nulls_first")
    2070        nulls_last = not nulls_first
    2071        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2072        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2073        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2074
    2075        this = self.sql(expression, "this")
    2076
    2077        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2078        nulls_sort_change = ""
    2079        if nulls_first and (
    2080            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2081        ):
    2082            nulls_sort_change = " NULLS FIRST"
    2083        elif (
    2084            nulls_last
    2085            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2086            and not nulls_are_last
    2087        ):
    2088            nulls_sort_change = " NULLS LAST"
    2089
    2090        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2091        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2092            window = expression.find_ancestor(exp.Window, exp.Select)
    2093            if isinstance(window, exp.Window) and window.args.get("spec"):
    2094                self.unsupported(
    2095                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2096                )
    2097                nulls_sort_change = ""
    2098            elif self.NULL_ORDERING_SUPPORTED is None:
    2099                if expression.this.is_int:
    2100                    self.unsupported(
    2101                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2102                    )
    2103                elif not isinstance(expression.this, exp.Rand):
    2104                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2105                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2106                nulls_sort_change = ""
    2107
    2108        with_fill = self.sql(expression, "with_fill")
    2109        with_fill = f" {with_fill}" if with_fill else ""
    2110
    2111        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

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

    2113    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2114        partition = self.partition_by_sql(expression)
    2115        order = self.sql(expression, "order")
    2116        measures = self.expressions(expression, key="measures")
    2117        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2118        rows = self.sql(expression, "rows")
    2119        rows = self.seg(rows) if rows else ""
    2120        after = self.sql(expression, "after")
    2121        after = self.seg(after) if after else ""
    2122        pattern = self.sql(expression, "pattern")
    2123        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2124        definition_sqls = [
    2125            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2126            for definition in expression.args.get("define", [])
    2127        ]
    2128        definitions = self.expressions(sqls=definition_sqls)
    2129        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2130        body = "".join(
    2131            (
    2132                partition,
    2133                order,
    2134                measures,
    2135                rows,
    2136                after,
    2137                pattern,
    2138                define,
    2139            )
    2140        )
    2141        alias = self.sql(expression, "alias")
    2142        alias = f" {alias}" if alias else ""
    2143        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2145    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2146        limit = expression.args.get("limit")
    2147
    2148        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2149            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2150        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2151            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2152
    2153        options = self.expressions(expression, key="options")
    2154        if options:
    2155            options = f" OPTION{self.wrap(options)}"
    2156
    2157        return csv(
    2158            *sqls,
    2159            *[self.sql(join) for join in expression.args.get("joins") or []],
    2160            self.sql(expression, "connect"),
    2161            self.sql(expression, "match"),
    2162            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2163            self.sql(expression, "prewhere"),
    2164            self.sql(expression, "where"),
    2165            self.sql(expression, "group"),
    2166            self.sql(expression, "having"),
    2167            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2168            self.sql(expression, "order"),
    2169            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2170            *self.after_limit_modifiers(expression),
    2171            options,
    2172            sep="",
    2173        )

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

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

    2178    def offset_limit_modifiers(
    2179        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2180    ) -> t.List[str]:
    2181        return [
    2182            self.sql(expression, "offset") if fetch else self.sql(limit),
    2183            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2184        ]

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

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

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

    2191    def select_sql(self, expression: exp.Select) -> str:
    2192        into = expression.args.get("into")
    2193        if not self.SUPPORTS_SELECT_INTO and into:
    2194            into.pop()
    2195
    2196        hint = self.sql(expression, "hint")
    2197        distinct = self.sql(expression, "distinct")
    2198        distinct = f" {distinct}" if distinct else ""
    2199        kind = self.sql(expression, "kind")
    2200
    2201        limit = expression.args.get("limit")
    2202        if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP:
    2203            top = self.limit_sql(limit, top=True)
    2204            limit.pop()
    2205        else:
    2206            top = ""
    2207
    2208        expressions = self.expressions(expression)
    2209
    2210        if kind:
    2211            if kind in self.SELECT_KINDS:
    2212                kind = f" AS {kind}"
    2213            else:
    2214                if kind == "STRUCT":
    2215                    expressions = self.expressions(
    2216                        sqls=[
    2217                            self.sql(
    2218                                exp.Struct(
    2219                                    expressions=[
    2220                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2221                                        if isinstance(e, exp.Alias)
    2222                                        else e
    2223                                        for e in expression.expressions
    2224                                    ]
    2225                                )
    2226                            )
    2227                        ]
    2228                    )
    2229                kind = ""
    2230
    2231        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2232        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2233        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2234        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2235        sql = self.query_modifiers(
    2236            expression,
    2237            f"SELECT{top_distinct}{kind}{expressions}",
    2238            self.sql(expression, "into", comment=False),
    2239            self.sql(expression, "from", comment=False),
    2240        )
    2241
    2242        sql = self.prepend_ctes(expression, sql)
    2243
    2244        if not self.SUPPORTS_SELECT_INTO and into:
    2245            if into.args.get("temporary"):
    2246                table_kind = " TEMPORARY"
    2247            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2248                table_kind = " UNLOGGED"
    2249            else:
    2250                table_kind = ""
    2251            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2252
    2253        return sql

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

    2255    def schema_sql(self, expression: exp.Schema) -> str:
    2256        this = self.sql(expression, "this")
    2257        sql = self.schema_columns_sql(expression)
    2258        return f"{this} {sql}" if this and sql else this or sql

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

    2260    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2261        if expression.expressions:
    2262            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2263        return ""

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

    2265    def star_sql(self, expression: exp.Star) -> str:
    2266        except_ = self.expressions(expression, key="except", flat=True)
    2267        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    2268        replace = self.expressions(expression, key="replace", flat=True)
    2269        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    2270        return f"*{except_}{replace}"

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

    2272    def parameter_sql(self, expression: exp.Parameter) -> str:
    2273        this = self.sql(expression, "this")
    2274        return f"{self.PARAMETER_TOKEN}{this}"

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

    2276    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2277        this = self.sql(expression, "this")
    2278        kind = expression.text("kind")
    2279        if kind:
    2280            kind = f"{kind}."
    2281        return f"@@{kind}{this}"

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

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

    2286    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2287        alias = self.sql(expression, "alias")
    2288        alias = f"{sep}{alias}" if alias else ""
    2289
    2290        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2291        pivots = f" {pivots}" if pivots else ""
    2292
    2293        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2294        return self.prepend_ctes(expression, sql)

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

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

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

    2300    def set_operations(self, expression: exp.Union) -> str:
    2301        sqls: t.List[str] = []
    2302        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    2303
    2304        while stack:
    2305            node = stack.pop()
    2306
    2307            if isinstance(node, exp.Union):
    2308                stack.append(node.expression)
    2309                stack.append(
    2310                    self.maybe_comment(
    2311                        getattr(self, f"{node.key}_op")(node),
    2312                        expression=node.this,
    2313                        comments=node.comments,
    2314                    )
    2315                )
    2316                stack.append(node.this)
    2317            else:
    2318                sqls.append(self.sql(node))
    2319
    2320        this = self.sep().join(sqls)
    2321        this = self.query_modifiers(expression, this)
    2322        return self.prepend_ctes(expression, this)

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

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

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

    2327    def union_op(self, expression: exp.Union) -> str:
    2328        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2329        kind = kind if expression.args.get("distinct") else " ALL"
    2330        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2331        return f"UNION{kind}{by_name}"

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

    2333    def unnest_sql(self, expression: exp.Unnest) -> str:
    2334        args = self.expressions(expression, flat=True)
    2335
    2336        alias = expression.args.get("alias")
    2337        offset = expression.args.get("offset")
    2338
    2339        if self.UNNEST_WITH_ORDINALITY:
    2340            if alias and isinstance(offset, exp.Expression):
    2341                alias.append("columns", offset)
    2342
    2343        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2344            columns = alias.columns
    2345            alias = self.sql(columns[0]) if columns else ""
    2346        else:
    2347            alias = self.sql(alias)
    2348
    2349        alias = f" AS {alias}" if alias else alias
    2350        if self.UNNEST_WITH_ORDINALITY:
    2351            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2352        else:
    2353            if isinstance(offset, exp.Expression):
    2354                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2355            elif offset:
    2356                suffix = f"{alias} WITH OFFSET"
    2357            else:
    2358                suffix = alias
    2359
    2360        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2369    def window_sql(self, expression: exp.Window) -> str:
    2370        this = self.sql(expression, "this")
    2371        partition = self.partition_by_sql(expression)
    2372        order = expression.args.get("order")
    2373        order = self.order_sql(order, flat=True) if order else ""
    2374        spec = self.sql(expression, "spec")
    2375        alias = self.sql(expression, "alias")
    2376        over = self.sql(expression, "over") or "OVER"
    2377
    2378        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2379
    2380        first = expression.args.get("first")
    2381        if first is None:
    2382            first = ""
    2383        else:
    2384            first = "FIRST" if first else "LAST"
    2385
    2386        if not partition and not order and not spec and alias:
    2387            return f"{this} {alias}"
    2388
    2389        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2390        return f"{this} ({args})"

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

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

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

    2396    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2397        kind = self.sql(expression, "kind")
    2398        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2399        end = (
    2400            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2401            or "CURRENT ROW"
    2402        )
    2403        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2410    def between_sql(self, expression: exp.Between) -> str:
    2411        this = self.sql(expression, "this")
    2412        low = self.sql(expression, "low")
    2413        high = self.sql(expression, "high")
    2414        return f"{this} BETWEEN {low} AND {high}"

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

    2416    def bracket_sql(self, expression: exp.Bracket) -> str:
    2417        expressions = apply_index_offset(
    2418            expression.this,
    2419            expression.expressions,
    2420            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2421        )
    2422        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2423        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    2428    def any_sql(self, expression: exp.Any) -> str:
    2429        this = self.sql(expression, "this")
    2430        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2431            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2432                this = self.wrap(this)
    2433            return f"ANY{this}"
    2434        return f"ANY {this}"

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

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

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

    2439    def case_sql(self, expression: exp.Case) -> str:
    2440        this = self.sql(expression, "this")
    2441        statements = [f"CASE {this}" if this else "CASE"]
    2442
    2443        for e in expression.args["ifs"]:
    2444            statements.append(f"WHEN {self.sql(e, 'this')}")
    2445            statements.append(f"THEN {self.sql(e, 'true')}")
    2446
    2447        default = self.sql(expression, "default")
    2448
    2449        if default:
    2450            statements.append(f"ELSE {default}")
    2451
    2452        statements.append("END")
    2453
    2454        if self.pretty and self.text_width(statements) > self.max_text_width:
    2455            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2456
    2457        return " ".join(statements)

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

    2459    def constraint_sql(self, expression: exp.Constraint) -> str:
    2460        this = self.sql(expression, "this")
    2461        expressions = self.expressions(expression, flat=True)
    2462        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2469    def extract_sql(self, expression: exp.Extract) -> str:
    2470        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2471        expression_sql = self.sql(expression, "expression")
    2472        return f"EXTRACT({this} FROM {expression_sql})"

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

    2474    def trim_sql(self, expression: exp.Trim) -> str:
    2475        trim_type = self.sql(expression, "position")
    2476
    2477        if trim_type == "LEADING":
    2478            return self.func("LTRIM", expression.this)
    2479        elif trim_type == "TRAILING":
    2480            return self.func("RTRIM", expression.this)
    2481        else:
    2482            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

    2484    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2485        args = expression.expressions
    2486        if isinstance(expression, exp.ConcatWs):
    2487            args = args[1:]  # Skip the delimiter
    2488
    2489        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2490            args = [exp.cast(e, "text") for e in args]
    2491
    2492        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2493            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2494
    2495        return args

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

    2497    def concat_sql(self, expression: exp.Concat) -> str:
    2498        expressions = self.convert_concat_args(expression)
    2499
    2500        # Some dialects don't allow a single-argument CONCAT call
    2501        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2502            return self.sql(expressions[0])
    2503
    2504        return self.func("CONCAT", *expressions)

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

    2506    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2507        return self.func(
    2508            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2509        )

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

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

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

    2515    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2516        expressions = self.expressions(expression, flat=True)
    2517        reference = self.sql(expression, "reference")
    2518        reference = f" {reference}" if reference else ""
    2519        delete = self.sql(expression, "delete")
    2520        delete = f" ON DELETE {delete}" if delete else ""
    2521        update = self.sql(expression, "update")
    2522        update = f" ON UPDATE {update}" if update else ""
    2523        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2525    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2526        expressions = self.expressions(expression, flat=True)
    2527        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2528        options = f" {options}" if options else ""
    2529        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

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

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

    2546    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2547        if isinstance(expression, exp.JSONPathPart):
    2548            transform = self.TRANSFORMS.get(expression.__class__)
    2549            if not callable(transform):
    2550                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2551                return ""
    2552
    2553            return transform(self, expression)
    2554
    2555        if isinstance(expression, int):
    2556            return str(expression)
    2557
    2558        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2559            escaped = expression.replace("'", "\\'")
    2560            escaped = f"\\'{expression}\\'"
    2561        else:
    2562            escaped = expression.replace('"', '\\"')
    2563            escaped = f'"{escaped}"'
    2564
    2565        return escaped

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

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

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

    2570    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2571        null_handling = expression.args.get("null_handling")
    2572        null_handling = f" {null_handling}" if null_handling else ""
    2573
    2574        unique_keys = expression.args.get("unique_keys")
    2575        if unique_keys is not None:
    2576            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2577        else:
    2578            unique_keys = ""
    2579
    2580        return_type = self.sql(expression, "return_type")
    2581        return_type = f" RETURNING {return_type}" if return_type else ""
    2582        encoding = self.sql(expression, "encoding")
    2583        encoding = f" ENCODING {encoding}" if encoding else ""
    2584
    2585        return self.func(
    2586            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2587            *expression.expressions,
    2588            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2589        )

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

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

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

    2594    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2595        null_handling = expression.args.get("null_handling")
    2596        null_handling = f" {null_handling}" if null_handling else ""
    2597        return_type = self.sql(expression, "return_type")
    2598        return_type = f" RETURNING {return_type}" if return_type else ""
    2599        strict = " STRICT" if expression.args.get("strict") else ""
    2600        return self.func(
    2601            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2602        )

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

    2604    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2605        this = self.sql(expression, "this")
    2606        order = self.sql(expression, "order")
    2607        null_handling = expression.args.get("null_handling")
    2608        null_handling = f" {null_handling}" if null_handling else ""
    2609        return_type = self.sql(expression, "return_type")
    2610        return_type = f" RETURNING {return_type}" if return_type else ""
    2611        strict = " STRICT" if expression.args.get("strict") else ""
    2612        return self.func(
    2613            "JSON_ARRAYAGG",
    2614            this,
    2615            suffix=f"{order}{null_handling}{return_type}{strict})",
    2616        )

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

    2618    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2619        path = self.sql(expression, "path")
    2620        path = f" PATH {path}" if path else ""
    2621        nested_schema = self.sql(expression, "nested_schema")
    2622
    2623        if nested_schema:
    2624            return f"NESTED{path} {nested_schema}"
    2625
    2626        this = self.sql(expression, "this")
    2627        kind = self.sql(expression, "kind")
    2628        kind = f" {kind}" if kind else ""
    2629        return f"{this}{kind}{path}"

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

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

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

    2634    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2635        this = self.sql(expression, "this")
    2636        path = self.sql(expression, "path")
    2637        path = f", {path}" if path else ""
    2638        error_handling = expression.args.get("error_handling")
    2639        error_handling = f" {error_handling}" if error_handling else ""
    2640        empty_handling = expression.args.get("empty_handling")
    2641        empty_handling = f" {empty_handling}" if empty_handling else ""
    2642        schema = self.sql(expression, "schema")
    2643        return self.func(
    2644            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2645        )

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

    2647    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2648        this = self.sql(expression, "this")
    2649        kind = self.sql(expression, "kind")
    2650        path = self.sql(expression, "path")
    2651        path = f" {path}" if path else ""
    2652        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2653        return f"{this} {kind}{path}{as_json}"

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

    2655    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2656        this = self.sql(expression, "this")
    2657        path = self.sql(expression, "path")
    2658        path = f", {path}" if path else ""
    2659        expressions = self.expressions(expression)
    2660        with_ = (
    2661            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2662            if expressions
    2663            else ""
    2664        )
    2665        return f"OPENJSON({this}{path}){with_}"

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

    2667    def in_sql(self, expression: exp.In) -> str:
    2668        query = expression.args.get("query")
    2669        unnest = expression.args.get("unnest")
    2670        field = expression.args.get("field")
    2671        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2672
    2673        if query:
    2674            in_sql = self.wrap(self.sql(query))
    2675        elif unnest:
    2676            in_sql = self.in_unnest_op(unnest)
    2677        elif field:
    2678            in_sql = self.sql(field)
    2679        else:
    2680            in_sql = f"({self.expressions(expression, flat=True)})"
    2681
    2682        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2687    def interval_sql(self, expression: exp.Interval) -> str:
    2688        unit = self.sql(expression, "unit")
    2689        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2690            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    2691        unit = f" {unit}" if unit else ""
    2692
    2693        if self.SINGLE_STRING_INTERVAL:
    2694            this = expression.this.name if expression.this else ""
    2695            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2696
    2697        this = self.sql(expression, "this")
    2698        if this:
    2699            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2700            this = f" {this}" if unwrapped else f" ({this})"
    2701
    2702        return f"INTERVAL{this}{unit}"

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

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

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

    2707    def reference_sql(self, expression: exp.Reference) -> str:
    2708        this = self.sql(expression, "this")
    2709        expressions = self.expressions(expression, flat=True)
    2710        expressions = f"({expressions})" if expressions else ""
    2711        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2712        options = f" {options}" if options else ""
    2713        return f"REFERENCES {this}{expressions}{options}"

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

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

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

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

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

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

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

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

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

    2731    def alias_sql(self, expression: exp.Alias) -> str:
    2732        alias = self.sql(expression, "alias")
    2733        alias = f" AS {alias}" if alias else ""
    2734        return f"{self.sql(expression, 'this')}{alias}"

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

    2736    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    2737        alias = expression.args["alias"]
    2738        identifier_alias = isinstance(alias, exp.Identifier)
    2739
    2740        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2741            alias.replace(exp.Literal.string(alias.output_name))
    2742        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2743            alias.replace(exp.to_identifier(alias.output_name))
    2744
    2745        return self.alias_sql(expression)

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

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

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

    2750    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    2751        this = self.sql(expression, "this")
    2752        index = self.sql(expression, "expression")
    2753        return f"{this} AT {index}"

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

    2755    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2756        this = self.sql(expression, "this")
    2757        zone = self.sql(expression, "zone")
    2758        return f"{this} AT TIME ZONE {zone}"

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

    2760    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    2761        this = self.sql(expression, "this")
    2762        zone = self.sql(expression, "zone")
    2763        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

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

    2768    def and_sql(
    2769        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    2770    ) -> str:
    2771        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

    2773    def or_sql(
    2774        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    2775    ) -> str:
    2776        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

    2778    def xor_sql(
    2779        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    2780    ) -> str:
    2781        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

    2783    def connector_sql(
    2784        self,
    2785        expression: exp.Connector,
    2786        op: str,
    2787        stack: t.Optional[t.List[str | exp.Expression]] = None,
    2788    ) -> str:
    2789        if stack is not None:
    2790            if expression.expressions:
    2791                stack.append(self.expressions(expression, sep=f" {op} "))
    2792            else:
    2793                stack.append(expression.right)
    2794                if expression.comments:
    2795                    for comment in expression.comments:
    2796                        op += f" /*{self.pad_comment(comment)}*/"
    2797                stack.extend((op, expression.left))
    2798            return op
    2799
    2800        stack = [expression]
    2801        sqls: t.List[str] = []
    2802        ops = set()
    2803
    2804        while stack:
    2805            node = stack.pop()
    2806            if isinstance(node, exp.Connector):
    2807                ops.add(getattr(self, f"{node.key}_sql")(node, stack))
    2808            else:
    2809                sql = self.sql(node)
    2810                if sqls and sqls[-1] in ops:
    2811                    sqls[-1] += f" {sql}"
    2812                else:
    2813                    sqls.append(sql)
    2814
    2815        sep = "\n" if self.pretty and self.text_width(sqls) > self.max_text_width else " "
    2816        return sep.join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2836    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2837        format_sql = self.sql(expression, "format")
    2838        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2839        to_sql = self.sql(expression, "to")
    2840        to_sql = f" {to_sql}" if to_sql else ""
    2841        action = self.sql(expression, "action")
    2842        action = f" {action}" if action else ""
    2843        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

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

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

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

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

    2852    def collate_sql(self, expression: exp.Collate) -> str:
    2853        if self.COLLATE_IS_FUNC:
    2854            return self.function_fallback_sql(expression)
    2855        return self.binary(expression, "COLLATE")

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

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

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

    2860    def comment_sql(self, expression: exp.Comment) -> str:
    2861        this = self.sql(expression, "this")
    2862        kind = expression.args["kind"]
    2863        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2864        expression_sql = self.sql(expression, "expression")
    2865        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    2867    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2868        this = self.sql(expression, "this")
    2869        delete = " DELETE" if expression.args.get("delete") else ""
    2870        recompress = self.sql(expression, "recompress")
    2871        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2872        to_disk = self.sql(expression, "to_disk")
    2873        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2874        to_volume = self.sql(expression, "to_volume")
    2875        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2876        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2878    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2879        where = self.sql(expression, "where")
    2880        group = self.sql(expression, "group")
    2881        aggregates = self.expressions(expression, key="aggregates")
    2882        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2883
    2884        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2885            return f"TTL {self.expressions(expression, flat=True)}"
    2886
    2887        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2892    def commit_sql(self, expression: exp.Commit) -> str:
    2893        chain = expression.args.get("chain")
    2894        if chain is not None:
    2895            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2896
    2897        return f"COMMIT{chain or ''}"

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

    2899    def rollback_sql(self, expression: exp.Rollback) -> str:
    2900        savepoint = expression.args.get("savepoint")
    2901        savepoint = f" TO {savepoint}" if savepoint else ""
    2902        return f"ROLLBACK{savepoint}"

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

    2904    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2905        this = self.sql(expression, "this")
    2906
    2907        dtype = self.sql(expression, "dtype")
    2908        if dtype:
    2909            collate = self.sql(expression, "collate")
    2910            collate = f" COLLATE {collate}" if collate else ""
    2911            using = self.sql(expression, "using")
    2912            using = f" USING {using}" if using else ""
    2913            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2914
    2915        default = self.sql(expression, "default")
    2916        if default:
    2917            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2918
    2919        comment = self.sql(expression, "comment")
    2920        if comment:
    2921            return f"ALTER COLUMN {this} COMMENT {comment}"
    2922
    2923        if not expression.args.get("drop"):
    2924            self.unsupported("Unsupported ALTER COLUMN syntax")
    2925
    2926        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2928    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2929        if not self.RENAME_TABLE_WITH_DB:
    2930            # Remove db from tables
    2931            expression = expression.transform(
    2932                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2933            ).assert_is(exp.RenameTable)
    2934        this = self.sql(expression, "this")
    2935        return f"RENAME TO {this}"

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

    2937    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    2938        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2939        old_column = self.sql(expression, "this")
    2940        new_column = self.sql(expression, "to")
    2941        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

    2943    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2944        actions = expression.args["actions"]
    2945
    2946        if isinstance(actions[0], exp.ColumnDef):
    2947            actions = self.add_column_sql(expression)
    2948        elif isinstance(actions[0], exp.Schema):
    2949            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2950        elif isinstance(actions[0], exp.Delete):
    2951            actions = self.expressions(expression, key="actions", flat=True)
    2952        else:
    2953            actions = self.expressions(expression, key="actions", flat=True)
    2954
    2955        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2956        only = " ONLY" if expression.args.get("only") else ""
    2957        options = self.expressions(expression, key="options")
    2958        options = f", {options}" if options else ""
    2959        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"

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

    2961    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2962        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2963            return self.expressions(
    2964                expression,
    2965                key="actions",
    2966                prefix="ADD COLUMN ",
    2967            )
    2968        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    2970    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2971        expressions = self.expressions(expression)
    2972        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2973        return f"DROP{exists}{expressions}"

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

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

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

    2978    def distinct_sql(self, expression: exp.Distinct) -> str:
    2979        this = self.expressions(expression, flat=True)
    2980
    2981        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    2982            case = exp.case()
    2983            for arg in expression.expressions:
    2984                case = case.when(arg.is_(exp.null()), exp.null())
    2985            this = self.sql(case.else_(f"({this})"))
    2986
    2987        this = f" {this}" if this else ""
    2988
    2989        on = self.sql(expression, "on")
    2990        on = f" ON {on}" if on else ""
    2991        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2999    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3000        this_sql = self.sql(expression, "this")
    3001        expression_sql = self.sql(expression, "expression")
    3002        kind = "MAX" if expression.args.get("max") else "MIN"
    3003        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3005    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3006        return self.sql(
    3007            exp.Cast(
    3008                this=exp.Div(this=expression.this, expression=expression.expression),
    3009                to=exp.DataType(this=exp.DataType.Type.INT),
    3010            )
    3011        )

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

    3013    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3014        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3015            return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten()))
    3016        return self.binary(expression, "||")

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

    3018    def div_sql(self, expression: exp.Div) -> str:
    3019        l, r = expression.left, expression.right
    3020
    3021        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3022            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3023
    3024        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3025            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3026                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3027
    3028        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3029            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3030                return self.sql(
    3031                    exp.cast(
    3032                        l / r,
    3033                        to=exp.DataType.Type.BIGINT,
    3034                    )
    3035                )
    3036
    3037        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3072    def is_sql(self, expression: exp.Is) -> str:
    3073        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3074            return self.sql(
    3075                expression.this if expression.expression.this else exp.not_(expression.this)
    3076            )
    3077        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3118    def log_sql(self, expression: exp.Log) -> str:
    3119        this = expression.this
    3120        expr = expression.expression
    3121
    3122        if self.dialect.LOG_BASE_FIRST is False:
    3123            this, expr = expr, this
    3124        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3125            if this.name in ("2", "10"):
    3126                return self.func(f"LOG{this.name}", expr)
    3127
    3128            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3129
    3130        return self.func("LOG", this, expr)

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

    3132    def use_sql(self, expression: exp.Use) -> str:
    3133        kind = self.sql(expression, "kind")
    3134        kind = f" {kind}" if kind else ""
    3135        this = self.sql(expression, "this")
    3136        this = f" {this}" if this else ""
    3137        return f"USE{kind}{this}"

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

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

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

    3143    def function_fallback_sql(self, expression: exp.Func) -> str:
    3144        args = []
    3145
    3146        for key in expression.arg_types:
    3147            arg_value = expression.args.get(key)
    3148
    3149            if isinstance(arg_value, list):
    3150                for value in arg_value:
    3151                    args.append(value)
    3152            elif arg_value is not None:
    3153                args.append(arg_value)
    3154
    3155        if self.normalize_functions:
    3156            name = expression.sql_name()
    3157        else:
    3158            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3159
    3160        return self.func(name, *args)

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

    3162    def func(
    3163        self,
    3164        name: str,
    3165        *args: t.Optional[exp.Expression | str],
    3166        prefix: str = "(",
    3167        suffix: str = ")",
    3168    ) -> str:
    3169        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

    3171    def format_args(self, *args: t.Optional[str | exp.Expression]) -> str:
    3172        arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None)
    3173        if self.pretty and self.text_width(arg_sqls) > self.max_text_width:
    3174            return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True)
    3175        return ", ".join(arg_sqls)

def text_width(self, args: Iterable) -> int: View Source

    3177    def text_width(self, args: t.Iterable) -> int:
    3178        return sum(len(arg) for arg in args)

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

    3180    def format_time(
    3181        self,
    3182        expression: exp.Expression,
    3183        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3184        inverse_time_trie: t.Optional[t.Dict] = None,
    3185    ) -> t.Optional[str]:
    3186        return format_time(
    3187            self.sql(expression, "format"),
    3188            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3189            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3190        )

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

    3192    def expressions(
    3193        self,
    3194        expression: t.Optional[exp.Expression] = None,
    3195        key: t.Optional[str] = None,
    3196        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3197        flat: bool = False,
    3198        indent: bool = True,
    3199        skip_first: bool = False,
    3200        sep: str = ", ",
    3201        prefix: str = "",
    3202    ) -> str:
    3203        expressions = expression.args.get(key or "expressions") if expression else sqls
    3204
    3205        if not expressions:
    3206            return ""
    3207
    3208        if flat:
    3209            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3210
    3211        num_sqls = len(expressions)
    3212
    3213        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    3214        if self.pretty:
    3215            if self.leading_comma:
    3216                pad = " " * len(sep)
    3217            else:
    3218                stripped_sep = sep.strip()
    3219
    3220        result_sqls = []
    3221        for i, e in enumerate(expressions):
    3222            sql = self.sql(e, comment=False)
    3223            if not sql:
    3224                continue
    3225
    3226            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3227
    3228            if self.pretty:
    3229                if self.leading_comma:
    3230                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    3231                else:
    3232                    result_sqls.append(
    3233                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    3234                    )
    3235            else:
    3236                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3237
    3238        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    3239        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql

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

    3241    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3242        flat = flat or isinstance(expression.parent, exp.Properties)
    3243        expressions_sql = self.expressions(expression, flat=flat)
    3244        if flat:
    3245            return f"{op} {expressions_sql}"
    3246        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3248    def naked_property(self, expression: exp.Property) -> str:
    3249        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3250        if not property_name:
    3251            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3252        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3260    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3261        this = self.sql(expression, "this")
    3262        expressions = self.no_identify(self.expressions, expression)
    3263        expressions = (
    3264            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3265        )
    3266        return f"{this}{expressions}"

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

    3268    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3269        this = self.sql(expression, "this")
    3270        expressions = self.expressions(expression, flat=True)
    3271        return f"{this}({expressions})"

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

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

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

    3276    def when_sql(self, expression: exp.When) -> str:
    3277        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3278        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3279        condition = self.sql(expression, "condition")
    3280        condition = f" AND {condition}" if condition else ""
    3281
    3282        then_expression = expression.args.get("then")
    3283        if isinstance(then_expression, exp.Insert):
    3284            then = f"INSERT {self.sql(then_expression, 'this')}"
    3285            if "expression" in then_expression.args:
    3286                then += f" VALUES {self.sql(then_expression, 'expression')}"
    3287        elif isinstance(then_expression, exp.Update):
    3288            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3289                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3290            else:
    3291                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3292        else:
    3293            then = self.sql(then_expression)
    3294        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3296    def merge_sql(self, expression: exp.Merge) -> str:
    3297        table = expression.this
    3298        table_alias = ""
    3299
    3300        hints = table.args.get("hints")
    3301        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3302            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3303            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3304
    3305        this = self.sql(table)
    3306        using = f"USING {self.sql(expression, 'using')}"
    3307        on = f"ON {self.sql(expression, 'on')}"
    3308        expressions = self.expressions(expression, sep=" ")
    3309
    3310        return self.prepend_ctes(
    3311            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    3312        )

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

    3314    def tochar_sql(self, expression: exp.ToChar) -> str:
    3315        if expression.args.get("format"):
    3316            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3317
    3318        return self.sql(exp.cast(expression.this, "text"))

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

    3320    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3321        if not self.SUPPORTS_TO_NUMBER:
    3322            self.unsupported("Unsupported TO_NUMBER function")
    3323            return self.sql(exp.cast(expression.this, "double"))
    3324
    3325        fmt = expression.args.get("format")
    3326        if not fmt:
    3327            self.unsupported("Conversion format is required for TO_NUMBER")
    3328            return self.sql(exp.cast(expression.this, "double"))
    3329
    3330        return self.func("TO_NUMBER", expression.this, fmt)

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

    3332    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3333        this = self.sql(expression, "this")
    3334        kind = self.sql(expression, "kind")
    3335        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3336        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3337        return f"{this}({kind}{args})"

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

    3339    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3340        this = self.sql(expression, "this")
    3341        max = self.sql(expression, "max")
    3342        min = self.sql(expression, "min")
    3343        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3351    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3352        expressions = self.expressions(expression, key="expressions", flat=True)
    3353        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3354        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3355        buckets = self.sql(expression, "buckets")
    3356        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3358    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3359        this = self.sql(expression, "this")
    3360        having = self.sql(expression, "having")
    3361
    3362        if having:
    3363            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3364
    3365        return self.func("ANY_VALUE", this)

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

    3367    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3368        transform = self.func("TRANSFORM", *expression.expressions)
    3369        row_format_before = self.sql(expression, "row_format_before")
    3370        row_format_before = f" {row_format_before}" if row_format_before else ""
    3371        record_writer = self.sql(expression, "record_writer")
    3372        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3373        using = f" USING {self.sql(expression, 'command_script')}"
    3374        schema = self.sql(expression, "schema")
    3375        schema = f" AS {schema}" if schema else ""
    3376        row_format_after = self.sql(expression, "row_format_after")
    3377        row_format_after = f" {row_format_after}" if row_format_after else ""
    3378        record_reader = self.sql(expression, "record_reader")
    3379        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3380        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

    3382    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3383        key_block_size = self.sql(expression, "key_block_size")
    3384        if key_block_size:
    3385            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3386
    3387        using = self.sql(expression, "using")
    3388        if using:
    3389            return f"USING {using}"
    3390
    3391        parser = self.sql(expression, "parser")
    3392        if parser:
    3393            return f"WITH PARSER {parser}"
    3394
    3395        comment = self.sql(expression, "comment")
    3396        if comment:
    3397            return f"COMMENT {comment}"
    3398
    3399        visible = expression.args.get("visible")
    3400        if visible is not None:
    3401            return "VISIBLE" if visible else "INVISIBLE"
    3402
    3403        engine_attr = self.sql(expression, "engine_attr")
    3404        if engine_attr:
    3405            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3406
    3407        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3408        if secondary_engine_attr:
    3409            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3410
    3411        self.unsupported("Unsupported index constraint option.")
    3412        return ""

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

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

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

    3418    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3419        kind = self.sql(expression, "kind")
    3420        kind = f"{kind} INDEX" if kind else "INDEX"
    3421        this = self.sql(expression, "this")
    3422        this = f" {this}" if this else ""
    3423        index_type = self.sql(expression, "index_type")
    3424        index_type = f" USING {index_type}" if index_type else ""
    3425        schema = self.sql(expression, "schema")
    3426        schema = f" {schema}" if schema else ""
    3427        options = self.expressions(expression, key="options", sep=" ")
    3428        options = f" {options}" if options else ""
    3429        return f"{kind}{this}{index_type}{schema}{options}"

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

    3431    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3432        if self.NVL2_SUPPORTED:
    3433            return self.function_fallback_sql(expression)
    3434
    3435        case = exp.Case().when(
    3436            expression.this.is_(exp.null()).not_(copy=False),
    3437            expression.args["true"],
    3438            copy=False,
    3439        )
    3440        else_cond = expression.args.get("false")
    3441        if else_cond:
    3442            case.else_(else_cond, copy=False)
    3443
    3444        return self.sql(case)

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

    3446    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3447        this = self.sql(expression, "this")
    3448        expr = self.sql(expression, "expression")
    3449        iterator = self.sql(expression, "iterator")
    3450        condition = self.sql(expression, "condition")
    3451        condition = f" IF {condition}" if condition else ""
    3452        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3460    def predict_sql(self, expression: exp.Predict) -> str:
    3461        model = self.sql(expression, "this")
    3462        model = f"MODEL {model}"
    3463        table = self.sql(expression, "expression")
    3464        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3465        parameters = self.sql(expression, "params_struct")
    3466        return self.func("PREDICT", model, table, parameters or None)

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

    3468    def forin_sql(self, expression: exp.ForIn) -> str:
    3469        this = self.sql(expression, "this")
    3470        expression_sql = self.sql(expression, "expression")
    3471        return f"FOR {this} DO {expression_sql}"

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

    3473    def refresh_sql(self, expression: exp.Refresh) -> str:
    3474        this = self.sql(expression, "this")
    3475        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3476        return f"REFRESH {table}{this}"

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

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

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

    3481    def toarray_sql(self, expression: exp.ToArray) -> str:
    3482        arg = expression.this
    3483        if not arg.type:
    3484            from sqlglot.optimizer.annotate_types import annotate_types
    3485
    3486            arg = annotate_types(arg)
    3487
    3488        if arg.is_type(exp.DataType.Type.ARRAY):
    3489            return self.sql(arg)
    3490
    3491        cond_for_null = arg.is_(exp.null())
    3492        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

    3494    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3495        this = expression.this
    3496        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3497            return self.sql(this)
    3498
    3499        return self.sql(exp.cast(this, "time"))

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

    3501    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3502        this = expression.this
    3503        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3504            return self.sql(this)
    3505
    3506        return self.sql(exp.cast(this, "timestamp"))

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

    3508    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3509        this = expression.this
    3510        time_format = self.format_time(expression)
    3511
    3512        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3513            return self.sql(
    3514                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
    3515            )
    3516
    3517        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3518            return self.sql(this)
    3519
    3520        return self.sql(exp.cast(this, "date"))

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

    3522    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3523        return self.sql(
    3524            exp.func(
    3525                "DATEDIFF",
    3526                expression.this,
    3527                exp.cast(exp.Literal.string("1970-01-01"), "date"),
    3528                "day",
    3529            )
    3530        )

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

    3532    def lastday_sql(self, expression: exp.LastDay) -> str:
    3533        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3534            return self.function_fallback_sql(expression)
    3535
    3536        unit = expression.text("unit")
    3537        if unit and unit != "MONTH":
    3538            self.unsupported("Date parts are not supported in LAST_DAY.")
    3539
    3540        return self.func("LAST_DAY", expression.this)

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

    3542    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    3543        from sqlglot.dialects.dialect import unit_to_str
    3544
    3545        return self.func(
    3546            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    3547        )

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

    3549    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3550        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3551            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3552            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3553            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3554            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3555
    3556        from sqlglot.dialects import Dialect
    3557
    3558        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3559        if self.dialect.__class__ != Dialect:
    3560            self.unsupported("ARRAY_ANY is unsupported")
    3561
    3562        return self.function_fallback_sql(expression)

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

    3564    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3565        expression.set("is_end_exclusive", None)
    3566        return self.function_fallback_sql(expression)

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

    3568    def struct_sql(self, expression: exp.Struct) -> str:
    3569        expression.set(
    3570            "expressions",
    3571            [
    3572                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    3573                if isinstance(e, exp.PropertyEQ)
    3574                else e
    3575                for e in expression.expressions
    3576            ],
    3577        )
    3578
    3579        return self.function_fallback_sql(expression)

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

    3581    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3582        low = self.sql(expression, "this")
    3583        high = self.sql(expression, "expression")
    3584
    3585        return f"{low} TO {high}"

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

    3587    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3588        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3589        tables = f" {self.expressions(expression)}"
    3590
    3591        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3592
    3593        on_cluster = self.sql(expression, "cluster")
    3594        on_cluster = f" {on_cluster}" if on_cluster else ""
    3595
    3596        identity = self.sql(expression, "identity")
    3597        identity = f" {identity} IDENTITY" if identity else ""
    3598
    3599        option = self.sql(expression, "option")
    3600        option = f" {option}" if option else ""
    3601
    3602        partition = self.sql(expression, "partition")
    3603        partition = f" {partition}" if partition else ""
    3604
    3605        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

    3609    def convert_sql(self, expression: exp.Convert) -> str:
    3610        to = expression.this
    3611        value = expression.expression
    3612        style = expression.args.get("style")
    3613        safe = expression.args.get("safe")
    3614        strict = expression.args.get("strict")
    3615
    3616        if not to or not value:
    3617            return ""
    3618
    3619        # Retrieve length of datatype and override to default if not specified
    3620        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3621            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    3622
    3623        transformed: t.Optional[exp.Expression] = None
    3624        cast = exp.Cast if strict else exp.TryCast
    3625
    3626        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    3627        if isinstance(style, exp.Literal) and style.is_int:
    3628            from sqlglot.dialects.tsql import TSQL
    3629
    3630            style_value = style.name
    3631            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    3632            if not converted_style:
    3633                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    3634
    3635            fmt = exp.Literal.string(converted_style)
    3636
    3637            if to.this == exp.DataType.Type.DATE:
    3638                transformed = exp.StrToDate(this=value, format=fmt)
    3639            elif to.this == exp.DataType.Type.DATETIME:
    3640                transformed = exp.StrToTime(this=value, format=fmt)
    3641            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3642                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    3643            elif to.this == exp.DataType.Type.TEXT:
    3644                transformed = exp.TimeToStr(this=value, format=fmt)
    3645
    3646        if not transformed:
    3647            transformed = cast(this=value, to=to, safe=safe)
    3648
    3649        return self.sql(transformed)