sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

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

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

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

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

    574    def __init__(
    575        self,
    576        pretty: t.Optional[bool] = None,
    577        identify: str | bool = False,
    578        normalize: bool = False,
    579        pad: int = 2,
    580        indent: int = 2,
    581        normalize_functions: t.Optional[str | bool] = None,
    582        unsupported_level: ErrorLevel = ErrorLevel.WARN,
    583        max_unsupported: int = 3,
    584        leading_comma: bool = False,
    585        max_text_width: int = 80,
    586        comments: bool = True,
    587        dialect: DialectType = None,
    588    ):
    589        import sqlglot
    590        from sqlglot.dialects import Dialect
    591
    592        self.pretty = pretty if pretty is not None else sqlglot.pretty
    593        self.identify = identify
    594        self.normalize = normalize
    595        self.pad = pad
    596        self._indent = indent
    597        self.unsupported_level = unsupported_level
    598        self.max_unsupported = max_unsupported
    599        self.leading_comma = leading_comma
    600        self.max_text_width = max_text_width
    601        self.comments = comments
    602        self.dialect = Dialect.get_or_raise(dialect)
    603
    604        # This is both a Dialect property and a Generator argument, so we prioritize the latter
    605        self.normalize_functions = (
    606            self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions
    607        )
    608
    609        self.unsupported_messages: t.List[str] = []
    610        self._escaped_quote_end: str = (
    611            self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END
    612        )
    613        self._escaped_identifier_end: str = (
    614            self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END
    615        )
    616
    617        self._next_name = name_sequence("_t")

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <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.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ProjectionPolicyColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SecureProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TagColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXPLICIT_SET_OP = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathSubscript'>}
CAN_IMPLEMENT_ARRAY_ANY = False
SUPPORTS_TO_NUMBER = True
SET_OP_MODIFIERS = True
COPY_PARAMS_ARE_WRAPPED = True
COPY_PARAMS_EQ_REQUIRED = False
COPY_HAS_INTO_KEYWORD = True
TRY_SUPPORTED = True
SUPPORTS_UESCAPE = True
STAR_EXCEPT = 'EXCEPT'
HEX_FUNC = 'HEX'
WITH_PROPERTIES_PREFIX = 'WITH'
QUOTE_JSON_PATH = True
PARSE_JSON_NAME: Optional[str] = 'PARSE_JSON'
TYPE_MAPPING = {<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.StrictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
RESERVED_KEYWORDS: Set[str] = set()
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.SetOperation'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] = (<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES = {<Type.NVARCHAR: 'NVARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <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

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

    653    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    654        """Apply generic preprocessing transformations to a given expression."""
    655        if (
    656            not expression.parent
    657            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    658            and any(node.parent is not expression for node in expression.find_all(exp.With))
    659        ):
    660            from sqlglot.transforms import move_ctes_to_top_level
    661
    662            expression = move_ctes_to_top_level(expression)
    663
    664        if self.ENSURE_BOOLS:
    665            from sqlglot.transforms import ensure_bools
    666
    667            expression = ensure_bools(expression)
    668
    669        return expression

Apply generic preprocessing transformations to a given expression.

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

    671    def unsupported(self, message: str) -> None:
    672        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    673            raise UnsupportedError(message)
    674        self.unsupported_messages.append(message)

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

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

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

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

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

    682    def pad_comment(self, comment: str) -> str:
    683        comment = " " + comment if comment[0].strip() else comment
    684        comment = comment + " " if comment[-1].strip() else comment
    685        return comment

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

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

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

    721    def wrap(self, expression: exp.Expression | str) -> str:
    722        this_sql = (
    723            self.sql(expression)
    724            if isinstance(expression, exp.UNWRAPPED_QUERIES)
    725            else self.sql(expression, "this")
    726        )
    727        if not this_sql:
    728            return "()"
    729
    730        this_sql = self.indent(this_sql, level=1, pad=0)
    731        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    733    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    734        original = self.identify
    735        self.identify = False
    736        result = func(*args, **kwargs)
    737        self.identify = original
    738        return result

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

    740    def normalize_func(self, name: str) -> str:
    741        if self.normalize_functions == "upper" or self.normalize_functions is True:
    742            return name.upper()
    743        if self.normalize_functions == "lower":
    744            return name.lower()
    745        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

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

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

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

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

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

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

    813    def cache_sql(self, expression: exp.Cache) -> str:
    814        lazy = " LAZY" if expression.args.get("lazy") else ""
    815        table = self.sql(expression, "this")
    816        options = expression.args.get("options")
    817        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    818        sql = self.sql(expression, "expression")
    819        sql = f" AS{self.sep()}{sql}" if sql else ""
    820        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    821        return self.prepend_ctes(expression, sql)

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

    823    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    824        if isinstance(expression.parent, exp.Cast):
    825            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    826        default = "DEFAULT " if expression.args.get("default") else ""
    827        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    829    def column_parts(self, expression: exp.Column) -> str:
    830        return ".".join(
    831            self.sql(part)
    832            for part in (
    833                expression.args.get("catalog"),
    834                expression.args.get("db"),
    835                expression.args.get("table"),
    836                expression.args.get("this"),
    837            )
    838            if part
    839        )

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

    841    def column_sql(self, expression: exp.Column) -> str:
    842        join_mark = " (+)" if expression.args.get("join_mark") else ""
    843
    844        if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS:
    845            join_mark = ""
    846            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    847
    848        return f"{self.column_parts(expression)}{join_mark}"

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

    850    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    851        this = self.sql(expression, "this")
    852        this = f" {this}" if this else ""
    853        position = self.sql(expression, "position")
    854        return f"{position}{this}"

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

    856    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    857        column = self.sql(expression, "this")
    858        kind = self.sql(expression, "kind")
    859        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    860        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    861        kind = f"{sep}{kind}" if kind else ""
    862        constraints = f" {constraints}" if constraints else ""
    863        position = self.sql(expression, "position")
    864        position = f" {position}" if position else ""
    865
    866        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    867            kind = ""
    868
    869        return f"{exists}{column}{kind}{constraints}{position}"

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

    871    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    872        this = self.sql(expression, "this")
    873        kind_sql = self.sql(expression, "kind").strip()
    874        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    876    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    877        this = self.sql(expression, "this")
    878        if expression.args.get("not_null"):
    879            persisted = " PERSISTED NOT NULL"
    880        elif expression.args.get("persisted"):
    881            persisted = " PERSISTED"
    882        else:
    883            persisted = ""
    884        return f"AS {this}{persisted}"

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

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

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

    889    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    890        if isinstance(expression.this, list):
    891            this = self.wrap(self.expressions(expression, key="this", flat=True))
    892        else:
    893            this = self.sql(expression, "this")
    894
    895        return f"COMPRESS {this}"

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

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

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

    930    def generatedasrowcolumnconstraint_sql(
    931        self, expression: exp.GeneratedAsRowColumnConstraint
    932    ) -> str:
    933        start = "START" if expression.args.get("start") else "END"
    934        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    935        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    937    def periodforsystemtimeconstraint_sql(
    938        self, expression: exp.PeriodForSystemTimeConstraint
    939    ) -> str:
    940        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

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

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

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

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

    948    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    949        desc = expression.args.get("desc")
    950        if desc is not None:
    951            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    952        return "PRIMARY KEY"

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

    954    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    955        this = self.sql(expression, "this")
    956        this = f" {this}" if this else ""
    957        index_type = expression.args.get("index_type")
    958        index_type = f" USING {index_type}" if index_type else ""
    959        on_conflict = self.sql(expression, "on_conflict")
    960        on_conflict = f" {on_conflict}" if on_conflict else ""
    961        return f"UNIQUE{this}{index_type}{on_conflict}"

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

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

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

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

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

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

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

    1084    def clone_sql(self, expression: exp.Clone) -> str:
    1085        this = self.sql(expression, "this")
    1086        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1087        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1088        return f"{shallow}{keyword} {this}"

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

    1090    def describe_sql(self, expression: exp.Describe) -> str:
    1091        style = expression.args.get("style")
    1092        style = f" {style}" if style else ""
    1093        return f"DESCRIBE{style} {self.sql(expression, 'this')}"

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

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

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

    1099    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1100        with_ = self.sql(expression, "with")
    1101        if with_:
    1102            sql = f"{with_}{self.sep()}{sql}"
    1103        return sql

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

    1105    def with_sql(self, expression: exp.With) -> str:
    1106        sql = self.expressions(expression, flat=True)
    1107        recursive = (
    1108            "RECURSIVE "
    1109            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1110            else ""
    1111        )
    1112
    1113        return f"WITH {recursive}{sql}"

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

    1115    def cte_sql(self, expression: exp.CTE) -> str:
    1116        alias = self.sql(expression, "alias")
    1117
    1118        materialized = expression.args.get("materialized")
    1119        if materialized is False:
    1120            materialized = "NOT MATERIALIZED "
    1121        elif materialized:
    1122            materialized = "MATERIALIZED "
    1123
    1124        return f"{alias} AS {materialized or ''}{self.wrap(expression)}"

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

    1126    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1127        alias = self.sql(expression, "this")
    1128        columns = self.expressions(expression, key="columns", flat=True)
    1129        columns = f"({columns})" if columns else ""
    1130
    1131        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1132            columns = ""
    1133            self.unsupported("Named columns are not supported in table alias.")
    1134
    1135        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1136            alias = self._next_name()
    1137
    1138        return f"{alias}{columns}"

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

    1140    def bitstring_sql(self, expression: exp.BitString) -> str:
    1141        this = self.sql(expression, "this")
    1142        if self.dialect.BIT_START:
    1143            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1144        return f"{int(this, 2)}"

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

    1146    def hexstring_sql(self, expression: exp.HexString) -> str:
    1147        this = self.sql(expression, "this")
    1148        if self.dialect.HEX_START:
    1149            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1150        return f"{int(this, 16)}"

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

    1152    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1153        this = self.sql(expression, "this")
    1154        if self.dialect.BYTE_START:
    1155            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1156        return this

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

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

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

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

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

    1185    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1186        this = self.sql(expression, "this")
    1187        specifier = self.sql(expression, "expression")
    1188        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1189        return f"{this}{specifier}"

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

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

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

    1228    def directory_sql(self, expression: exp.Directory) -> str:
    1229        local = "LOCAL " if expression.args.get("local") else ""
    1230        row_format = self.sql(expression, "row_format")
    1231        row_format = f" {row_format}" if row_format else ""
    1232        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1234    def delete_sql(self, expression: exp.Delete) -> str:
    1235        this = self.sql(expression, "this")
    1236        this = f" FROM {this}" if this else ""
    1237        using = self.sql(expression, "using")
    1238        using = f" USING {using}" if using else ""
    1239        where = self.sql(expression, "where")
    1240        returning = self.sql(expression, "returning")
    1241        limit = self.sql(expression, "limit")
    1242        tables = self.expressions(expression, key="tables")
    1243        tables = f" {tables}" if tables else ""
    1244        if self.RETURNING_END:
    1245            expression_sql = f"{this}{using}{where}{returning}{limit}"
    1246        else:
    1247            expression_sql = f"{returning}{this}{using}{where}{limit}"
    1248        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1250    def drop_sql(self, expression: exp.Drop) -> str:
    1251        this = self.sql(expression, "this")
    1252        expressions = self.expressions(expression, flat=True)
    1253        expressions = f" ({expressions})" if expressions else ""
    1254        kind = expression.args["kind"]
    1255        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1256        on_cluster = self.sql(expression, "cluster")
    1257        on_cluster = f" {on_cluster}" if on_cluster else ""
    1258        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1259        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1260        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1261        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1262        purge = " PURGE" if expression.args.get("purge") else ""
    1263        return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"

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

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

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

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

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

    1271    def fetch_sql(self, expression: exp.Fetch) -> str:
    1272        direction = expression.args.get("direction")
    1273        direction = f" {direction}" if direction else ""
    1274        count = expression.args.get("count")
    1275        count = f" {count}" if count else ""
    1276        if expression.args.get("percent"):
    1277            count = f"{count} PERCENT"
    1278        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1279        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1281    def filter_sql(self, expression: exp.Filter) -> str:
    1282        if self.AGGREGATE_FILTER_SUPPORTED:
    1283            this = self.sql(expression, "this")
    1284            where = self.sql(expression, "expression").strip()
    1285            return f"{this} FILTER({where})"
    1286
    1287        agg = expression.this
    1288        agg_arg = agg.this
    1289        cond = expression.expression.this
    1290        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1291        return self.sql(agg)

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

    1293    def hint_sql(self, expression: exp.Hint) -> str:
    1294        if not self.QUERY_HINTS:
    1295            self.unsupported("Hints are not supported")
    1296            return ""
    1297
    1298        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1300    def indexparameters_sql(self, expression: exp.IndexParameters) -> str:
    1301        using = self.sql(expression, "using")
    1302        using = f" USING {using}" if using else ""
    1303        columns = self.expressions(expression, key="columns", flat=True)
    1304        columns = f"({columns})" if columns else ""
    1305        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1306        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1307        where = self.sql(expression, "where")
    1308        include = self.expressions(expression, key="include", flat=True)
    1309        if include:
    1310            include = f" INCLUDE ({include})"
    1311        with_storage = self.expressions(expression, key="with_storage", flat=True)
    1312        with_storage = f" WITH ({with_storage})" if with_storage else ""
    1313        tablespace = self.sql(expression, "tablespace")
    1314        tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else ""
    1315        on = self.sql(expression, "on")
    1316        on = f" ON {on}" if on else ""
    1317
    1318        return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"

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

    1320    def index_sql(self, expression: exp.Index) -> str:
    1321        unique = "UNIQUE " if expression.args.get("unique") else ""
    1322        primary = "PRIMARY " if expression.args.get("primary") else ""
    1323        amp = "AMP " if expression.args.get("amp") else ""
    1324        name = self.sql(expression, "this")
    1325        name = f"{name} " if name else ""
    1326        table = self.sql(expression, "table")
    1327        table = f"{self.INDEX_ON} {table}" if table else ""
    1328
    1329        index = "INDEX " if not table else ""
    1330
    1331        params = self.sql(expression, "params")
    1332        return f"{unique}{primary}{amp}{index}{name}{table}{params}"

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

    1334    def identifier_sql(self, expression: exp.Identifier) -> str:
    1335        text = expression.name
    1336        lower = text.lower()
    1337        text = lower if self.normalize and not expression.quoted else text
    1338        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1339        if (
    1340            expression.quoted
    1341            or self.dialect.can_identify(text, self.identify)
    1342            or lower in self.RESERVED_KEYWORDS
    1343            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1344        ):
    1345            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1346        return text

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

    1348    def hex_sql(self, expression: exp.Hex) -> str:
    1349        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1350        if self.dialect.HEX_LOWERCASE:
    1351            text = self.func("LOWER", text)
    1352
    1353        return text

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

    1355    def lowerhex_sql(self, expression: exp.LowerHex) -> str:
    1356        text = self.func(self.HEX_FUNC, self.sql(expression, "this"))
    1357        if not self.dialect.HEX_LOWERCASE:
    1358            text = self.func("LOWER", text)
    1359        return text

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

    1361    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1362        input_format = self.sql(expression, "input_format")
    1363        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1364        output_format = self.sql(expression, "output_format")
    1365        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1366        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1375    def properties_sql(self, expression: exp.Properties) -> str:
    1376        root_properties = []
    1377        with_properties = []
    1378
    1379        for p in expression.expressions:
    1380            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1381            if p_loc == exp.Properties.Location.POST_WITH:
    1382                with_properties.append(p)
    1383            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1384                root_properties.append(p)
    1385
    1386        root_props = self.root_properties(exp.Properties(expressions=root_properties))
    1387        with_props = self.with_properties(exp.Properties(expressions=with_properties))
    1388
    1389        if root_props and with_props and not self.pretty:
    1390            with_props = " " + with_props
    1391
    1392        return root_props + with_props

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

    1394    def root_properties(self, properties: exp.Properties) -> str:
    1395        if properties.expressions:
    1396            return self.expressions(properties, indent=False, sep=" ")
    1397        return ""

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

    1399    def properties(
    1400        self,
    1401        properties: exp.Properties,
    1402        prefix: str = "",
    1403        sep: str = ", ",
    1404        suffix: str = "",
    1405        wrapped: bool = True,
    1406    ) -> str:
    1407        if properties.expressions:
    1408            expressions = self.expressions(properties, sep=sep, indent=False)
    1409            if expressions:
    1410                expressions = self.wrap(expressions) if wrapped else expressions
    1411                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1412        return ""

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

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

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

    1417    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1418        properties_locs = defaultdict(list)
    1419        for p in properties.expressions:
    1420            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1421            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1422                properties_locs[p_loc].append(p)
    1423            else:
    1424                self.unsupported(f"Unsupported property {p.key}")
    1425
    1426        return properties_locs

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

    1428    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1429        if isinstance(expression.this, exp.Dot):
    1430            return self.sql(expression, "this")
    1431        return f"'{expression.name}'" if string_key else expression.name

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

    1433    def property_sql(self, expression: exp.Property) -> str:
    1434        property_cls = expression.__class__
    1435        if property_cls == exp.Property:
    1436            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1437
    1438        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1439        if not property_name:
    1440            self.unsupported(f"Unsupported property {expression.key}")
    1441
    1442        return f"{property_name}={self.sql(expression, 'this')}"

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

    1444    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1445        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1446            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1447            options = f" {options}" if options else ""
    1448
    1449            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1450            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1451                like = f"({like})"
    1452
    1453            return like
    1454
    1455        if expression.expressions:
    1456            self.unsupported("Transpilation of LIKE property options is unsupported")
    1457
    1458        select = exp.select("*").from_(expression.this).limit(0)
    1459        return f"AS {self.sql(select)}"

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

    1461    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1462        no = "NO " if expression.args.get("no") else ""
    1463        protection = " PROTECTION" if expression.args.get("protection") else ""
    1464        return f"{no}FALLBACK{protection}"

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

    1466    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1467        no = "NO " if expression.args.get("no") else ""
    1468        local = expression.args.get("local")
    1469        local = f"{local} " if local else ""
    1470        dual = "DUAL " if expression.args.get("dual") else ""
    1471        before = "BEFORE " if expression.args.get("before") else ""
    1472        after = "AFTER " if expression.args.get("after") else ""
    1473        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1475    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1476        freespace = self.sql(expression, "this")
    1477        percent = " PERCENT" if expression.args.get("percent") else ""
    1478        return f"FREESPACE={freespace}{percent}"

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

    1480    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1481        if expression.args.get("default"):
    1482            property = "DEFAULT"
    1483        elif expression.args.get("on"):
    1484            property = "ON"
    1485        else:
    1486            property = "OFF"
    1487        return f"CHECKSUM={property}"

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

    1489    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1490        if expression.args.get("no"):
    1491            return "NO MERGEBLOCKRATIO"
    1492        if expression.args.get("default"):
    1493            return "DEFAULT MERGEBLOCKRATIO"
    1494
    1495        percent = " PERCENT" if expression.args.get("percent") else ""
    1496        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1498    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1499        default = expression.args.get("default")
    1500        minimum = expression.args.get("minimum")
    1501        maximum = expression.args.get("maximum")
    1502        if default or minimum or maximum:
    1503            if default:
    1504                prop = "DEFAULT"
    1505            elif minimum:
    1506                prop = "MINIMUM"
    1507            else:
    1508                prop = "MAXIMUM"
    1509            return f"{prop} DATABLOCKSIZE"
    1510        units = expression.args.get("units")
    1511        units = f" {units}" if units else ""
    1512        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1514    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1515        autotemp = expression.args.get("autotemp")
    1516        always = expression.args.get("always")
    1517        default = expression.args.get("default")
    1518        manual = expression.args.get("manual")
    1519        never = expression.args.get("never")
    1520
    1521        if autotemp is not None:
    1522            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1523        elif always:
    1524            prop = "ALWAYS"
    1525        elif default:
    1526            prop = "DEFAULT"
    1527        elif manual:
    1528            prop = "MANUAL"
    1529        elif never:
    1530            prop = "NEVER"
    1531        return f"BLOCKCOMPRESSION={prop}"

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

    1533    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1534        no = expression.args.get("no")
    1535        no = " NO" if no else ""
    1536        concurrent = expression.args.get("concurrent")
    1537        concurrent = " CONCURRENT" if concurrent else ""
    1538        target = self.sql(expression, "target")
    1539        target = f" {target}" if target else ""
    1540        return f"WITH{no}{concurrent} ISOLATED LOADING{target}"

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

    1542    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1543        if isinstance(expression.this, list):
    1544            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1545        if expression.this:
    1546            modulus = self.sql(expression, "this")
    1547            remainder = self.sql(expression, "expression")
    1548            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1549
    1550        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1551        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1552        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1554    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1555        this = self.sql(expression, "this")
    1556
    1557        for_values_or_default = expression.expression
    1558        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1559            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1560        else:
    1561            for_values_or_default = " DEFAULT"
    1562
    1563        return f"PARTITION OF {this}{for_values_or_default}"

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

    1565    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1566        kind = expression.args.get("kind")
    1567        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1568        for_or_in = expression.args.get("for_or_in")
    1569        for_or_in = f" {for_or_in}" if for_or_in else ""
    1570        lock_type = expression.args.get("lock_type")
    1571        override = " OVERRIDE" if expression.args.get("override") else ""
    1572        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1574    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1575        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1576        statistics = expression.args.get("statistics")
    1577        statistics_sql = ""
    1578        if statistics is not None:
    1579            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1580        return f"{data_sql}{statistics_sql}"

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

    1582    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1583        this = self.sql(expression, "this")
    1584        this = f"HISTORY_TABLE={this}" if this else ""
    1585        data_consistency: t.Optional[str] = self.sql(expression, "data_consistency")
    1586        data_consistency = (
    1587            f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None
    1588        )
    1589        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    1590        retention_period = (
    1591            f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None
    1592        )
    1593
    1594        if this:
    1595            on_sql = self.func("ON", this, data_consistency, retention_period)
    1596        else:
    1597            on_sql = "ON" if expression.args.get("on") else "OFF"
    1598
    1599        sql = f"SYSTEM_VERSIONING={on_sql}"
    1600
    1601        return f"WITH({sql})" if expression.args.get("with") else sql

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

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

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

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

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

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

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

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

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

    1648    def kill_sql(self, expression: exp.Kill) -> str:
    1649        kind = self.sql(expression, "kind")
    1650        kind = f" {kind}" if kind else ""
    1651        this = self.sql(expression, "this")
    1652        this = f" {this}" if this else ""
    1653        return f"KILL{kind}{this}"

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

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

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

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

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

    1661    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1662        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1663
    1664        constraint = self.sql(expression, "constraint")
    1665        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1666
    1667        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1668        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1669        action = self.sql(expression, "action")
    1670
    1671        expressions = self.expressions(expression, flat=True)
    1672        if expressions:
    1673            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1674            expressions = f" {set_keyword}{expressions}"
    1675
    1676        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"

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

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

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

    1681    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1682        fields = self.sql(expression, "fields")
    1683        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1684        escaped = self.sql(expression, "escaped")
    1685        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1686        items = self.sql(expression, "collection_items")
    1687        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1688        keys = self.sql(expression, "map_keys")
    1689        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1690        lines = self.sql(expression, "lines")
    1691        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1692        null = self.sql(expression, "null")
    1693        null = f" NULL DEFINED AS {null}" if null else ""
    1694        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1699    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1700        this = f"{self.sql(expression, 'this')} INDEX"
    1701        target = self.sql(expression, "target")
    1702        target = f" FOR {target}" if target else ""
    1703        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1705    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1706        this = self.sql(expression, "this")
    1707        kind = self.sql(expression, "kind")
    1708        expr = self.sql(expression, "expression")
    1709        return f"{this} ({kind} => {expr})"

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

    1711    def table_parts(self, expression: exp.Table) -> str:
    1712        return ".".join(
    1713            self.sql(part)
    1714            for part in (
    1715                expression.args.get("catalog"),
    1716                expression.args.get("db"),
    1717                expression.args.get("this"),
    1718            )
    1719            if part is not None
    1720        )

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

    1722    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1723        table = self.table_parts(expression)
    1724        only = "ONLY " if expression.args.get("only") else ""
    1725        partition = self.sql(expression, "partition")
    1726        partition = f" {partition}" if partition else ""
    1727        version = self.sql(expression, "version")
    1728        version = f" {version}" if version else ""
    1729        alias = self.sql(expression, "alias")
    1730        alias = f"{sep}{alias}" if alias else ""
    1731        hints = self.expressions(expression, key="hints", sep=" ")
    1732        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1733        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1734        pivots = f" {pivots}" if pivots else ""
    1735        joins = self.indent(
    1736            self.expressions(expression, key="joins", sep="", flat=True), skip_first=True
    1737        )
    1738        laterals = self.expressions(expression, key="laterals", sep="")
    1739
    1740        file_format = self.sql(expression, "format")
    1741        if file_format:
    1742            pattern = self.sql(expression, "pattern")
    1743            pattern = f", PATTERN => {pattern}" if pattern else ""
    1744            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1745
    1746        ordinality = expression.args.get("ordinality") or ""
    1747        if ordinality:
    1748            ordinality = f" WITH ORDINALITY{alias}"
    1749            alias = ""
    1750
    1751        when = self.sql(expression, "when")
    1752        if when:
    1753            table = f"{table} {when}"
    1754
    1755        changes = self.sql(expression, "changes")
    1756        changes = f" {changes}" if changes else ""
    1757
    1758        return f"{only}{table}{changes}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"

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

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

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

    1801    def pivot_sql(self, expression: exp.Pivot) -> str:
    1802        expressions = self.expressions(expression, flat=True)
    1803
    1804        if expression.this:
    1805            this = self.sql(expression, "this")
    1806            if not expressions:
    1807                return f"UNPIVOT {this}"
    1808
    1809            on = f"{self.seg('ON')} {expressions}"
    1810            using = self.expressions(expression, key="using", flat=True)
    1811            using = f"{self.seg('USING')} {using}" if using else ""
    1812            group = self.sql(expression, "group")
    1813            return f"PIVOT {this}{on}{using}{group}"
    1814
    1815        alias = self.sql(expression, "alias")
    1816        alias = f" AS {alias}" if alias else ""
    1817        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    1818        field = self.sql(expression, "field")
    1819        include_nulls = expression.args.get("include_nulls")
    1820        if include_nulls is not None:
    1821            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1822        else:
    1823            nulls = ""
    1824        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1826    def version_sql(self, expression: exp.Version) -> str:
    1827        this = f"FOR {expression.name}"
    1828        kind = expression.text("kind")
    1829        expr = self.sql(expression, "expression")
    1830        return f"{this} {kind} {expr}"

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

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

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

    1835    def update_sql(self, expression: exp.Update) -> str:
    1836        this = self.sql(expression, "this")
    1837        set_sql = self.expressions(expression, flat=True)
    1838        from_sql = self.sql(expression, "from")
    1839        where_sql = self.sql(expression, "where")
    1840        returning = self.sql(expression, "returning")
    1841        order = self.sql(expression, "order")
    1842        limit = self.sql(expression, "limit")
    1843        if self.RETURNING_END:
    1844            expression_sql = f"{from_sql}{where_sql}{returning}"
    1845        else:
    1846            expression_sql = f"{returning}{from_sql}{where_sql}"
    1847        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1848        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

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

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

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

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

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

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

    1950    def connect_sql(self, expression: exp.Connect) -> str:
    1951        start = self.sql(expression, "start")
    1952        start = self.seg(f"START WITH {start}") if start else ""
    1953        nocycle = " NOCYCLE" if expression.args.get("nocycle") else ""
    1954        connect = self.sql(expression, "connect")
    1955        connect = self.seg(f"CONNECT BY{nocycle} {connect}")
    1956        return start + connect

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

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

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

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

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

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

    2012    def lateral_op(self, expression: exp.Lateral) -> str:
    2013        cross_apply = expression.args.get("cross_apply")
    2014
    2015        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    2016        if cross_apply is True:
    2017            op = "INNER JOIN "
    2018        elif cross_apply is False:
    2019            op = "LEFT JOIN "
    2020        else:
    2021            op = ""
    2022
    2023        return f"{op}LATERAL"

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

    2025    def lateral_sql(self, expression: exp.Lateral) -> str:
    2026        this = self.sql(expression, "this")
    2027
    2028        if expression.args.get("view"):
    2029            alias = expression.args["alias"]
    2030            columns = self.expressions(alias, key="columns", flat=True)
    2031            table = f" {alias.name}" if alias.name else ""
    2032            columns = f" AS {columns}" if columns else ""
    2033            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    2034            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    2035
    2036        alias = self.sql(expression, "alias")
    2037        alias = f" AS {alias}" if alias else ""
    2038        return f"{self.lateral_op(expression)} {this}{alias}"

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

    2040    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    2041        this = self.sql(expression, "this")
    2042
    2043        args = [
    2044            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    2045            for e in (expression.args.get(k) for k in ("offset", "expression"))
    2046            if e
    2047        ]
    2048
    2049        args_sql = ", ".join(self.sql(e) for e in args)
    2050        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    2051        expressions = self.expressions(expression, flat=True)
    2052        expressions = f" BY {expressions}" if expressions else ""
    2053
    2054        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    2056    def offset_sql(self, expression: exp.Offset) -> str:
    2057        this = self.sql(expression, "this")
    2058        value = expression.expression
    2059        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    2060        expressions = self.expressions(expression, flat=True)
    2061        expressions = f" BY {expressions}" if expressions else ""
    2062        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    2064    def setitem_sql(self, expression: exp.SetItem) -> str:
    2065        kind = self.sql(expression, "kind")
    2066        kind = f"{kind} " if kind else ""
    2067        this = self.sql(expression, "this")
    2068        expressions = self.expressions(expression)
    2069        collate = self.sql(expression, "collate")
    2070        collate = f" COLLATE {collate}" if collate else ""
    2071        global_ = "GLOBAL " if expression.args.get("global") else ""
    2072        return f"{global_}{kind}{this}{expressions}{collate}"

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

    2074    def set_sql(self, expression: exp.Set) -> str:
    2075        expressions = (
    2076            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    2077        )
    2078        tag = " TAG" if expression.args.get("tag") else ""
    2079        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    2084    def lock_sql(self, expression: exp.Lock) -> str:
    2085        if not self.LOCKING_READS_SUPPORTED:
    2086            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    2087            return ""
    2088
    2089        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    2090        expressions = self.expressions(expression, flat=True)
    2091        expressions = f" OF {expressions}" if expressions else ""
    2092        wait = expression.args.get("wait")
    2093
    2094        if wait is not None:
    2095            if isinstance(wait, exp.Literal):
    2096                wait = f" WAIT {self.sql(wait)}"
    2097            else:
    2098                wait = " NOWAIT" if wait else " SKIP LOCKED"
    2099
    2100        return f"{lock_type}{expressions}{wait or ''}"

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

    2102    def literal_sql(self, expression: exp.Literal) -> str:
    2103        text = expression.this or ""
    2104        if expression.is_string:
    2105            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    2106        return text

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

    2108    def escape_str(self, text: str, escape_backslash: bool = True) -> str:
    2109        if self.dialect.ESCAPED_SEQUENCES:
    2110            to_escaped = self.dialect.ESCAPED_SEQUENCES
    2111            text = "".join(
    2112                to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text
    2113            )
    2114
    2115        return self._replace_line_breaks(text).replace(
    2116            self.dialect.QUOTE_END, self._escaped_quote_end
    2117        )

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

    2119    def loaddata_sql(self, expression: exp.LoadData) -> str:
    2120        local = " LOCAL" if expression.args.get("local") else ""
    2121        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    2122        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    2123        this = f" INTO TABLE {self.sql(expression, 'this')}"
    2124        partition = self.sql(expression, "partition")
    2125        partition = f" {partition}" if partition else ""
    2126        input_format = self.sql(expression, "input_format")
    2127        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    2128        serde = self.sql(expression, "serde")
    2129        serde = f" SERDE {serde}" if serde else ""
    2130        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    2132    def null_sql(self, *_) -> str:
    2133        return "NULL"

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

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

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

    2138    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    2139        this = self.sql(expression, "this")
    2140        this = f"{this} " if this else this
    2141        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2142        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2143        interpolated_values = [
    2144            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
    2145            for named_expression in expression.args.get("interpolate") or []
    2146        ]
    2147        interpolate = (
    2148            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2149        )
    2150        return f"{order}{interpolate}"

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

    2152    def withfill_sql(self, expression: exp.WithFill) -> str:
    2153        from_sql = self.sql(expression, "from")
    2154        from_sql = f" FROM {from_sql}" if from_sql else ""
    2155        to_sql = self.sql(expression, "to")
    2156        to_sql = f" TO {to_sql}" if to_sql else ""
    2157        step_sql = self.sql(expression, "step")
    2158        step_sql = f" STEP {step_sql}" if step_sql else ""
    2159        return f"WITH FILL{from_sql}{to_sql}{step_sql}"

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

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

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

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

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

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

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

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

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

    2218    def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str:
    2219        window_frame = self.sql(expression, "window_frame")
    2220        window_frame = f"{window_frame} " if window_frame else ""
    2221
    2222        this = self.sql(expression, "this")
    2223
    2224        return f"{window_frame}{this}"

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

    2226    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2227        partition = self.partition_by_sql(expression)
    2228        order = self.sql(expression, "order")
    2229        measures = self.expressions(expression, key="measures")
    2230        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2231        rows = self.sql(expression, "rows")
    2232        rows = self.seg(rows) if rows else ""
    2233        after = self.sql(expression, "after")
    2234        after = self.seg(after) if after else ""
    2235        pattern = self.sql(expression, "pattern")
    2236        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2237        definition_sqls = [
    2238            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2239            for definition in expression.args.get("define", [])
    2240        ]
    2241        definitions = self.expressions(sqls=definition_sqls)
    2242        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2243        body = "".join(
    2244            (
    2245                partition,
    2246                order,
    2247                measures,
    2248                rows,
    2249                after,
    2250                pattern,
    2251                define,
    2252            )
    2253        )
    2254        alias = self.sql(expression, "alias")
    2255        alias = f" {alias}" if alias else ""
    2256        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2258    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2259        limit = expression.args.get("limit")
    2260
    2261        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2262            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2263        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2264            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2265
    2266        return csv(
    2267            *sqls,
    2268            *[self.sql(join) for join in expression.args.get("joins") or []],
    2269            self.sql(expression, "connect"),
    2270            self.sql(expression, "match"),
    2271            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2272            self.sql(expression, "prewhere"),
    2273            self.sql(expression, "where"),
    2274            self.sql(expression, "group"),
    2275            self.sql(expression, "having"),
    2276            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2277            self.sql(expression, "order"),
    2278            *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit),
    2279            *self.after_limit_modifiers(expression),
    2280            self.options_modifier(expression),
    2281            sep="",
    2282        )

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

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

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

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

    2291    def offset_limit_modifiers(
    2292        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2293    ) -> t.List[str]:
    2294        return [
    2295            self.sql(expression, "offset") if fetch else self.sql(limit),
    2296            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2297        ]

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

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

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

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

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

    2368    def schema_sql(self, expression: exp.Schema) -> str:
    2369        this = self.sql(expression, "this")
    2370        sql = self.schema_columns_sql(expression)
    2371        return f"{this} {sql}" if this and sql else this or sql

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

    2373    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2374        if expression.expressions:
    2375            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2376        return ""

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

    2378    def star_sql(self, expression: exp.Star) -> str:
    2379        except_ = self.expressions(expression, key="except", flat=True)
    2380        except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else ""
    2381        replace = self.expressions(expression, key="replace", flat=True)
    2382        replace = f"{self.seg('REPLACE')} ({replace})" if replace else ""
    2383        rename = self.expressions(expression, key="rename", flat=True)
    2384        rename = f"{self.seg('RENAME')} ({rename})" if rename else ""
    2385        return f"*{except_}{replace}{rename}"

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

    2387    def parameter_sql(self, expression: exp.Parameter) -> str:
    2388        this = self.sql(expression, "this")
    2389        return f"{self.PARAMETER_TOKEN}{this}"

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

    2391    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2392        this = self.sql(expression, "this")
    2393        kind = expression.text("kind")
    2394        if kind:
    2395            kind = f"{kind}."
    2396        return f"@@{kind}{this}"

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

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

    2401    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2402        alias = self.sql(expression, "alias")
    2403        alias = f"{sep}{alias}" if alias else ""
    2404
    2405        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2406        pivots = f" {pivots}" if pivots else ""
    2407
    2408        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2409        return self.prepend_ctes(expression, sql)

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

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

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

    2415    def set_operations(self, expression: exp.SetOperation) -> str:
    2416        if not self.SET_OP_MODIFIERS:
    2417            limit = expression.args.get("limit")
    2418            order = expression.args.get("order")
    2419
    2420            if limit or order:
    2421                select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False)
    2422
    2423                if limit:
    2424                    select = select.limit(limit.pop(), copy=False)
    2425                if order:
    2426                    select = select.order_by(order.pop(), copy=False)
    2427                return self.sql(select)
    2428
    2429        sqls: t.List[str] = []
    2430        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    2431
    2432        while stack:
    2433            node = stack.pop()
    2434
    2435            if isinstance(node, exp.SetOperation):
    2436                stack.append(node.expression)
    2437                stack.append(
    2438                    self.maybe_comment(
    2439                        getattr(self, f"{node.key}_op")(node),
    2440                        comments=node.comments,
    2441                        separated=True,
    2442                    )
    2443                )
    2444                stack.append(node.this)
    2445            else:
    2446                sqls.append(self.sql(node))
    2447
    2448        this = self.sep().join(sqls)
    2449        this = self.query_modifiers(expression, this)
    2450        return self.prepend_ctes(expression, this)

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

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

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

    2455    def union_op(self, expression: exp.SetOperation) -> str:
    2456        kind = " DISTINCT" if self.EXPLICIT_SET_OP else ""
    2457        kind = kind if expression.args.get("distinct") else " ALL"
    2458        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2459        return f"UNION{kind}{by_name}"

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

    2461    def unnest_sql(self, expression: exp.Unnest) -> str:
    2462        args = self.expressions(expression, flat=True)
    2463
    2464        alias = expression.args.get("alias")
    2465        offset = expression.args.get("offset")
    2466
    2467        if self.UNNEST_WITH_ORDINALITY:
    2468            if alias and isinstance(offset, exp.Expression):
    2469                alias.append("columns", offset)
    2470
    2471        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2472            columns = alias.columns
    2473            alias = self.sql(columns[0]) if columns else ""
    2474        else:
    2475            alias = self.sql(alias)
    2476
    2477        alias = f" AS {alias}" if alias else alias
    2478        if self.UNNEST_WITH_ORDINALITY:
    2479            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2480        else:
    2481            if isinstance(offset, exp.Expression):
    2482                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2483            elif offset:
    2484                suffix = f"{alias} WITH OFFSET"
    2485            else:
    2486                suffix = alias
    2487
    2488        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2497    def window_sql(self, expression: exp.Window) -> str:
    2498        this = self.sql(expression, "this")
    2499        partition = self.partition_by_sql(expression)
    2500        order = expression.args.get("order")
    2501        order = self.order_sql(order, flat=True) if order else ""
    2502        spec = self.sql(expression, "spec")
    2503        alias = self.sql(expression, "alias")
    2504        over = self.sql(expression, "over") or "OVER"
    2505
    2506        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2507
    2508        first = expression.args.get("first")
    2509        if first is None:
    2510            first = ""
    2511        else:
    2512            first = "FIRST" if first else "LAST"
    2513
    2514        if not partition and not order and not spec and alias:
    2515            return f"{this} {alias}"
    2516
    2517        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2518        return f"{this} ({args})"

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

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

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

    2524    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2525        kind = self.sql(expression, "kind")
    2526        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2527        end = (
    2528            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2529            or "CURRENT ROW"
    2530        )
    2531        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2538    def between_sql(self, expression: exp.Between) -> str:
    2539        this = self.sql(expression, "this")
    2540        low = self.sql(expression, "low")
    2541        high = self.sql(expression, "high")
    2542        return f"{this} BETWEEN {low} AND {high}"

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

    2544    def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]:
    2545        return apply_index_offset(
    2546            expression.this,
    2547            expression.expressions,
    2548            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2549        )

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

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

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

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

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

    2559    def any_sql(self, expression: exp.Any) -> str:
    2560        this = self.sql(expression, "this")
    2561        if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)):
    2562            if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2563                this = self.wrap(this)
    2564            return f"ANY{this}"
    2565        return f"ANY {this}"

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

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

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

    2570    def case_sql(self, expression: exp.Case) -> str:
    2571        this = self.sql(expression, "this")
    2572        statements = [f"CASE {this}" if this else "CASE"]
    2573
    2574        for e in expression.args["ifs"]:
    2575            statements.append(f"WHEN {self.sql(e, 'this')}")
    2576            statements.append(f"THEN {self.sql(e, 'true')}")
    2577
    2578        default = self.sql(expression, "default")
    2579
    2580        if default:
    2581            statements.append(f"ELSE {default}")
    2582
    2583        statements.append("END")
    2584
    2585        if self.pretty and self.too_wide(statements):
    2586            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2587
    2588        return " ".join(statements)

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

    2590    def constraint_sql(self, expression: exp.Constraint) -> str:
    2591        this = self.sql(expression, "this")
    2592        expressions = self.expressions(expression, flat=True)
    2593        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2600    def extract_sql(self, expression: exp.Extract) -> str:
    2601        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2602        expression_sql = self.sql(expression, "expression")
    2603        return f"EXTRACT({this} FROM {expression_sql})"

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

    2605    def trim_sql(self, expression: exp.Trim) -> str:
    2606        trim_type = self.sql(expression, "position")
    2607
    2608        if trim_type == "LEADING":
    2609            return self.func("LTRIM", expression.this)
    2610        elif trim_type == "TRAILING":
    2611            return self.func("RTRIM", expression.this)
    2612        else:
    2613            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

    2615    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2616        args = expression.expressions
    2617        if isinstance(expression, exp.ConcatWs):
    2618            args = args[1:]  # Skip the delimiter
    2619
    2620        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2621            args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args]
    2622
    2623        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2624            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2625
    2626        return args

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

    2628    def concat_sql(self, expression: exp.Concat) -> str:
    2629        expressions = self.convert_concat_args(expression)
    2630
    2631        # Some dialects don't allow a single-argument CONCAT call
    2632        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2633            return self.sql(expressions[0])
    2634
    2635        return self.func("CONCAT", *expressions)

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

    2637    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2638        return self.func(
    2639            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2640        )

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

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

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

    2646    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2647        expressions = self.expressions(expression, flat=True)
    2648        reference = self.sql(expression, "reference")
    2649        reference = f" {reference}" if reference else ""
    2650        delete = self.sql(expression, "delete")
    2651        delete = f" ON DELETE {delete}" if delete else ""
    2652        update = self.sql(expression, "update")
    2653        update = f" ON UPDATE {update}" if update else ""
    2654        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2656    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2657        expressions = self.expressions(expression, flat=True)
    2658        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2659        options = f" {options}" if options else ""
    2660        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

    2673    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2674        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2675        if self.QUOTE_JSON_PATH:
    2676            path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}"
    2677
    2678        return path

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

    2680    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2681        if isinstance(expression, exp.JSONPathPart):
    2682            transform = self.TRANSFORMS.get(expression.__class__)
    2683            if not callable(transform):
    2684                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2685                return ""
    2686
    2687            return transform(self, expression)
    2688
    2689        if isinstance(expression, int):
    2690            return str(expression)
    2691
    2692        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2693            escaped = expression.replace("'", "\\'")
    2694            escaped = f"\\'{expression}\\'"
    2695        else:
    2696            escaped = expression.replace('"', '\\"')
    2697            escaped = f'"{escaped}"'
    2698
    2699        return escaped

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

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

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

    2704    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2705        null_handling = expression.args.get("null_handling")
    2706        null_handling = f" {null_handling}" if null_handling else ""
    2707
    2708        unique_keys = expression.args.get("unique_keys")
    2709        if unique_keys is not None:
    2710            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2711        else:
    2712            unique_keys = ""
    2713
    2714        return_type = self.sql(expression, "return_type")
    2715        return_type = f" RETURNING {return_type}" if return_type else ""
    2716        encoding = self.sql(expression, "encoding")
    2717        encoding = f" ENCODING {encoding}" if encoding else ""
    2718
    2719        return self.func(
    2720            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2721            *expression.expressions,
    2722            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2723        )

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

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

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

    2728    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2729        null_handling = expression.args.get("null_handling")
    2730        null_handling = f" {null_handling}" if null_handling else ""
    2731        return_type = self.sql(expression, "return_type")
    2732        return_type = f" RETURNING {return_type}" if return_type else ""
    2733        strict = " STRICT" if expression.args.get("strict") else ""
    2734        return self.func(
    2735            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2736        )

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

    2738    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2739        this = self.sql(expression, "this")
    2740        order = self.sql(expression, "order")
    2741        null_handling = expression.args.get("null_handling")
    2742        null_handling = f" {null_handling}" if null_handling else ""
    2743        return_type = self.sql(expression, "return_type")
    2744        return_type = f" RETURNING {return_type}" if return_type else ""
    2745        strict = " STRICT" if expression.args.get("strict") else ""
    2746        return self.func(
    2747            "JSON_ARRAYAGG",
    2748            this,
    2749            suffix=f"{order}{null_handling}{return_type}{strict})",
    2750        )

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

    2752    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2753        path = self.sql(expression, "path")
    2754        path = f" PATH {path}" if path else ""
    2755        nested_schema = self.sql(expression, "nested_schema")
    2756
    2757        if nested_schema:
    2758            return f"NESTED{path} {nested_schema}"
    2759
    2760        this = self.sql(expression, "this")
    2761        kind = self.sql(expression, "kind")
    2762        kind = f" {kind}" if kind else ""
    2763        return f"{this}{kind}{path}"

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

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

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

    2768    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2769        this = self.sql(expression, "this")
    2770        path = self.sql(expression, "path")
    2771        path = f", {path}" if path else ""
    2772        error_handling = expression.args.get("error_handling")
    2773        error_handling = f" {error_handling}" if error_handling else ""
    2774        empty_handling = expression.args.get("empty_handling")
    2775        empty_handling = f" {empty_handling}" if empty_handling else ""
    2776        schema = self.sql(expression, "schema")
    2777        return self.func(
    2778            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2779        )

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

    2781    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2782        this = self.sql(expression, "this")
    2783        kind = self.sql(expression, "kind")
    2784        path = self.sql(expression, "path")
    2785        path = f" {path}" if path else ""
    2786        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2787        return f"{this} {kind}{path}{as_json}"

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

    2789    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2790        this = self.sql(expression, "this")
    2791        path = self.sql(expression, "path")
    2792        path = f", {path}" if path else ""
    2793        expressions = self.expressions(expression)
    2794        with_ = (
    2795            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2796            if expressions
    2797            else ""
    2798        )
    2799        return f"OPENJSON({this}{path}){with_}"

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

    2801    def in_sql(self, expression: exp.In) -> str:
    2802        query = expression.args.get("query")
    2803        unnest = expression.args.get("unnest")
    2804        field = expression.args.get("field")
    2805        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2806
    2807        if query:
    2808            in_sql = self.sql(query)
    2809        elif unnest:
    2810            in_sql = self.in_unnest_op(unnest)
    2811        elif field:
    2812            in_sql = self.sql(field)
    2813        else:
    2814            in_sql = f"({self.expressions(expression, flat=True)})"
    2815
    2816        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2821    def interval_sql(self, expression: exp.Interval) -> str:
    2822        unit = self.sql(expression, "unit")
    2823        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2824            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    2825        unit = f" {unit}" if unit else ""
    2826
    2827        if self.SINGLE_STRING_INTERVAL:
    2828            this = expression.this.name if expression.this else ""
    2829            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2830
    2831        this = self.sql(expression, "this")
    2832        if this:
    2833            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2834            this = f" {this}" if unwrapped else f" ({this})"
    2835
    2836        return f"INTERVAL{this}{unit}"

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

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

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

    2841    def reference_sql(self, expression: exp.Reference) -> str:
    2842        this = self.sql(expression, "this")
    2843        expressions = self.expressions(expression, flat=True)
    2844        expressions = f"({expressions})" if expressions else ""
    2845        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2846        options = f" {options}" if options else ""
    2847        return f"REFERENCES {this}{expressions}{options}"

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

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

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

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

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

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

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

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

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

    2865    def alias_sql(self, expression: exp.Alias) -> str:
    2866        alias = self.sql(expression, "alias")
    2867        alias = f" AS {alias}" if alias else ""
    2868        return f"{self.sql(expression, 'this')}{alias}"

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

    2870    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    2871        alias = expression.args["alias"]
    2872        identifier_alias = isinstance(alias, exp.Identifier)
    2873
    2874        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2875            alias.replace(exp.Literal.string(alias.output_name))
    2876        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2877            alias.replace(exp.to_identifier(alias.output_name))
    2878
    2879        return self.alias_sql(expression)

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

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

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

    2884    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    2885        this = self.sql(expression, "this")
    2886        index = self.sql(expression, "expression")
    2887        return f"{this} AT {index}"

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

    2889    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2890        this = self.sql(expression, "this")
    2891        zone = self.sql(expression, "zone")
    2892        return f"{this} AT TIME ZONE {zone}"

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

    2894    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    2895        this = self.sql(expression, "this")
    2896        zone = self.sql(expression, "zone")
    2897        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

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

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

    2907    def or_sql(
    2908        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    2909    ) -> str:
    2910        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

    2912    def xor_sql(
    2913        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    2914    ) -> str:
    2915        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2971    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2972        format_sql = self.sql(expression, "format")
    2973        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2974        to_sql = self.sql(expression, "to")
    2975        to_sql = f" {to_sql}" if to_sql else ""
    2976        action = self.sql(expression, "action")
    2977        action = f" {action}" if action else ""
    2978        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

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

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

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

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

    2987    def collate_sql(self, expression: exp.Collate) -> str:
    2988        if self.COLLATE_IS_FUNC:
    2989            return self.function_fallback_sql(expression)
    2990        return self.binary(expression, "COLLATE")

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

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

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

    2995    def comment_sql(self, expression: exp.Comment) -> str:
    2996        this = self.sql(expression, "this")
    2997        kind = expression.args["kind"]
    2998        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    2999        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    3000        expression_sql = self.sql(expression, "expression")
    3001        return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"

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

    3003    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    3004        this = self.sql(expression, "this")
    3005        delete = " DELETE" if expression.args.get("delete") else ""
    3006        recompress = self.sql(expression, "recompress")
    3007        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    3008        to_disk = self.sql(expression, "to_disk")
    3009        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    3010        to_volume = self.sql(expression, "to_volume")
    3011        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    3012        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    3014    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    3015        where = self.sql(expression, "where")
    3016        group = self.sql(expression, "group")
    3017        aggregates = self.expressions(expression, key="aggregates")
    3018        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    3019
    3020        if not (where or group or aggregates) and len(expression.expressions) == 1:
    3021            return f"TTL {self.expressions(expression, flat=True)}"
    3022
    3023        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    3028    def commit_sql(self, expression: exp.Commit) -> str:
    3029        chain = expression.args.get("chain")
    3030        if chain is not None:
    3031            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    3032
    3033        return f"COMMIT{chain or ''}"

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

    3035    def rollback_sql(self, expression: exp.Rollback) -> str:
    3036        savepoint = expression.args.get("savepoint")
    3037        savepoint = f" TO {savepoint}" if savepoint else ""
    3038        return f"ROLLBACK{savepoint}"

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

    3040    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3041        this = self.sql(expression, "this")
    3042
    3043        dtype = self.sql(expression, "dtype")
    3044        if dtype:
    3045            collate = self.sql(expression, "collate")
    3046            collate = f" COLLATE {collate}" if collate else ""
    3047            using = self.sql(expression, "using")
    3048            using = f" USING {using}" if using else ""
    3049            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    3050
    3051        default = self.sql(expression, "default")
    3052        if default:
    3053            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3054
    3055        comment = self.sql(expression, "comment")
    3056        if comment:
    3057            return f"ALTER COLUMN {this} COMMENT {comment}"
    3058
    3059        allow_null = expression.args.get("allow_null")
    3060        drop = expression.args.get("drop")
    3061
    3062        if not drop and not allow_null:
    3063            self.unsupported("Unsupported ALTER COLUMN syntax")
    3064
    3065        if allow_null is not None:
    3066            keyword = "DROP" if drop else "SET"
    3067            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3068
    3069        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    3071    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3072        this = self.sql(expression, "this")
    3073        if not isinstance(expression.this, exp.Var):
    3074            this = f"KEY DISTKEY {this}"
    3075        return f"ALTER DISTSTYLE {this}"

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

    3077    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3078        compound = " COMPOUND" if expression.args.get("compound") else ""
    3079        this = self.sql(expression, "this")
    3080        expressions = self.expressions(expression, flat=True)
    3081        expressions = f"({expressions})" if expressions else ""
    3082        return f"ALTER{compound} SORTKEY {this or expressions}"

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

    3084    def renametable_sql(self, expression: exp.RenameTable) -> str:
    3085        if not self.RENAME_TABLE_WITH_DB:
    3086            # Remove db from tables
    3087            expression = expression.transform(
    3088                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3089            ).assert_is(exp.RenameTable)
    3090        this = self.sql(expression, "this")
    3091        return f"RENAME TO {this}"

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

    3093    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3094        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3095        old_column = self.sql(expression, "this")
    3096        new_column = self.sql(expression, "to")
    3097        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

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

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

    3103    def altertable_sql(self, expression: exp.AlterTable) -> str:
    3104        actions = expression.args["actions"]
    3105
    3106        if isinstance(actions[0], exp.ColumnDef):
    3107            actions = self.add_column_sql(expression)
    3108        elif isinstance(actions[0], exp.Schema):
    3109            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3110        elif isinstance(actions[0], exp.Delete):
    3111            actions = self.expressions(expression, key="actions", flat=True)
    3112        else:
    3113            actions = self.expressions(expression, key="actions", flat=True)
    3114
    3115        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3116        on_cluster = self.sql(expression, "cluster")
    3117        on_cluster = f" {on_cluster}" if on_cluster else ""
    3118        only = " ONLY" if expression.args.get("only") else ""
    3119        options = self.expressions(expression, key="options")
    3120        options = f", {options}" if options else ""
    3121        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"

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

    3123    def add_column_sql(self, expression: exp.AlterTable) -> str:
    3124        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3125            return self.expressions(
    3126                expression,
    3127                key="actions",
    3128                prefix="ADD COLUMN ",
    3129            )
    3130        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    3132    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3133        expressions = self.expressions(expression)
    3134        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3135        return f"DROP{exists}{expressions}"

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

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

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

    3140    def distinct_sql(self, expression: exp.Distinct) -> str:
    3141        this = self.expressions(expression, flat=True)
    3142
    3143        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3144            case = exp.case()
    3145            for arg in expression.expressions:
    3146                case = case.when(arg.is_(exp.null()), exp.null())
    3147            this = self.sql(case.else_(f"({this})"))
    3148
    3149        this = f" {this}" if this else ""
    3150
    3151        on = self.sql(expression, "on")
    3152        on = f" ON {on}" if on else ""
    3153        return f"DISTINCT{this}{on}"

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

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

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

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

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

    3161    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3162        this_sql = self.sql(expression, "this")
    3163        expression_sql = self.sql(expression, "expression")
    3164        kind = "MAX" if expression.args.get("max") else "MIN"
    3165        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3167    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3168        return self.sql(
    3169            exp.Cast(
    3170                this=exp.Div(this=expression.this, expression=expression.expression),
    3171                to=exp.DataType(this=exp.DataType.Type.INT),
    3172            )
    3173        )

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

    3175    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3176        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3177            return self.func(
    3178                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3179            )
    3180        return self.binary(expression, "||")

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

    3182    def div_sql(self, expression: exp.Div) -> str:
    3183        l, r = expression.left, expression.right
    3184
    3185        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3186            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3187
    3188        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3189            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3190                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3191
    3192        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3193            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3194                return self.sql(
    3195                    exp.cast(
    3196                        l / r,
    3197                        to=exp.DataType.Type.BIGINT,
    3198                    )
    3199                )
    3200
    3201        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3236    def is_sql(self, expression: exp.Is) -> str:
    3237        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3238            return self.sql(
    3239                expression.this if expression.expression.this else exp.not_(expression.this)
    3240            )
    3241        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3282    def try_sql(self, expression: exp.Try) -> str:
    3283        if not self.TRY_SUPPORTED:
    3284            self.unsupported("Unsupported TRY function")
    3285            return self.sql(expression, "this")
    3286
    3287        return self.func("TRY", expression.this)

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

    3289    def log_sql(self, expression: exp.Log) -> str:
    3290        this = expression.this
    3291        expr = expression.expression
    3292
    3293        if self.dialect.LOG_BASE_FIRST is False:
    3294            this, expr = expr, this
    3295        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3296            if this.name in ("2", "10"):
    3297                return self.func(f"LOG{this.name}", expr)
    3298
    3299            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3300
    3301        return self.func("LOG", this, expr)

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

    3303    def use_sql(self, expression: exp.Use) -> str:
    3304        kind = self.sql(expression, "kind")
    3305        kind = f" {kind}" if kind else ""
    3306        this = self.sql(expression, "this")
    3307        this = f" {this}" if this else ""
    3308        return f"USE{kind}{this}"

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

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

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

    3314    def function_fallback_sql(self, expression: exp.Func) -> str:
    3315        args = []
    3316
    3317        for key in expression.arg_types:
    3318            arg_value = expression.args.get(key)
    3319
    3320            if isinstance(arg_value, list):
    3321                for value in arg_value:
    3322                    args.append(value)
    3323            elif arg_value is not None:
    3324                args.append(arg_value)
    3325
    3326        if self.normalize_functions:
    3327            name = expression.sql_name()
    3328        else:
    3329            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3330
    3331        return self.func(name, *args)

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

    3333    def func(
    3334        self,
    3335        name: str,
    3336        *args: t.Optional[exp.Expression | str],
    3337        prefix: str = "(",
    3338        suffix: str = ")",
    3339    ) -> str:
    3340        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

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

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

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

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

    3351    def format_time(
    3352        self,
    3353        expression: exp.Expression,
    3354        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3355        inverse_time_trie: t.Optional[t.Dict] = None,
    3356    ) -> t.Optional[str]:
    3357        return format_time(
    3358            self.sql(expression, "format"),
    3359            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3360            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3361        )

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

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

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

    3422    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3423        flat = flat or isinstance(expression.parent, exp.Properties)
    3424        expressions_sql = self.expressions(expression, flat=flat)
    3425        if flat:
    3426            return f"{op} {expressions_sql}"
    3427        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3429    def naked_property(self, expression: exp.Property) -> str:
    3430        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3431        if not property_name:
    3432            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3433        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3441    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3442        this = self.sql(expression, "this")
    3443        expressions = self.no_identify(self.expressions, expression)
    3444        expressions = (
    3445            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3446        )
    3447        return f"{this}{expressions}"

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

    3449    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3450        this = self.sql(expression, "this")
    3451        expressions = self.expressions(expression, flat=True)
    3452        return f"{this}({expressions})"

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

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

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

    3457    def when_sql(self, expression: exp.When) -> str:
    3458        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3459        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3460        condition = self.sql(expression, "condition")
    3461        condition = f" AND {condition}" if condition else ""
    3462
    3463        then_expression = expression.args.get("then")
    3464        if isinstance(then_expression, exp.Insert):
    3465            this = self.sql(then_expression, "this")
    3466            this = f"INSERT {this}" if this else "INSERT"
    3467            then = self.sql(then_expression, "expression")
    3468            then = f"{this} VALUES {then}" if then else this
    3469        elif isinstance(then_expression, exp.Update):
    3470            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3471                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3472            else:
    3473                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3474        else:
    3475            then = self.sql(then_expression)
    3476        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3478    def merge_sql(self, expression: exp.Merge) -> str:
    3479        table = expression.this
    3480        table_alias = ""
    3481
    3482        hints = table.args.get("hints")
    3483        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3484            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3485            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3486
    3487        this = self.sql(table)
    3488        using = f"USING {self.sql(expression, 'using')}"
    3489        on = f"ON {self.sql(expression, 'on')}"
    3490        expressions = self.expressions(expression, sep=" ", indent=False)
    3491        sep = self.sep()
    3492
    3493        return self.prepend_ctes(
    3494            expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}"
    3495        )

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

    3497    def tochar_sql(self, expression: exp.ToChar) -> str:
    3498        if expression.args.get("format"):
    3499            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3500
    3501        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

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

    3503    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3504        if not self.SUPPORTS_TO_NUMBER:
    3505            self.unsupported("Unsupported TO_NUMBER function")
    3506            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3507
    3508        fmt = expression.args.get("format")
    3509        if not fmt:
    3510            self.unsupported("Conversion format is required for TO_NUMBER")
    3511            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3512
    3513        return self.func("TO_NUMBER", expression.this, fmt)

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

    3515    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3516        this = self.sql(expression, "this")
    3517        kind = self.sql(expression, "kind")
    3518        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3519        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3520        return f"{this}({kind}{args})"

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

    3522    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3523        this = self.sql(expression, "this")
    3524        max = self.sql(expression, "max")
    3525        min = self.sql(expression, "min")
    3526        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3534    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3535        expressions = self.expressions(expression, key="expressions", flat=True)
    3536        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3537        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3538        buckets = self.sql(expression, "buckets")
    3539        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3541    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3542        this = self.sql(expression, "this")
    3543        having = self.sql(expression, "having")
    3544
    3545        if having:
    3546            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3547
    3548        return self.func("ANY_VALUE", this)

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

    3550    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3551        transform = self.func("TRANSFORM", *expression.expressions)
    3552        row_format_before = self.sql(expression, "row_format_before")
    3553        row_format_before = f" {row_format_before}" if row_format_before else ""
    3554        record_writer = self.sql(expression, "record_writer")
    3555        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3556        using = f" USING {self.sql(expression, 'command_script')}"
    3557        schema = self.sql(expression, "schema")
    3558        schema = f" AS {schema}" if schema else ""
    3559        row_format_after = self.sql(expression, "row_format_after")
    3560        row_format_after = f" {row_format_after}" if row_format_after else ""
    3561        record_reader = self.sql(expression, "record_reader")
    3562        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3563        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

    3565    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3566        key_block_size = self.sql(expression, "key_block_size")
    3567        if key_block_size:
    3568            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3569
    3570        using = self.sql(expression, "using")
    3571        if using:
    3572            return f"USING {using}"
    3573
    3574        parser = self.sql(expression, "parser")
    3575        if parser:
    3576            return f"WITH PARSER {parser}"
    3577
    3578        comment = self.sql(expression, "comment")
    3579        if comment:
    3580            return f"COMMENT {comment}"
    3581
    3582        visible = expression.args.get("visible")
    3583        if visible is not None:
    3584            return "VISIBLE" if visible else "INVISIBLE"
    3585
    3586        engine_attr = self.sql(expression, "engine_attr")
    3587        if engine_attr:
    3588            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3589
    3590        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3591        if secondary_engine_attr:
    3592            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3593
    3594        self.unsupported("Unsupported index constraint option.")
    3595        return ""

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

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

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

    3601    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3602        kind = self.sql(expression, "kind")
    3603        kind = f"{kind} INDEX" if kind else "INDEX"
    3604        this = self.sql(expression, "this")
    3605        this = f" {this}" if this else ""
    3606        index_type = self.sql(expression, "index_type")
    3607        index_type = f" USING {index_type}" if index_type else ""
    3608        expressions = self.expressions(expression, flat=True)
    3609        expressions = f" ({expressions})" if expressions else ""
    3610        options = self.expressions(expression, key="options", sep=" ")
    3611        options = f" {options}" if options else ""
    3612        return f"{kind}{this}{index_type}{expressions}{options}"

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

    3614    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3615        if self.NVL2_SUPPORTED:
    3616            return self.function_fallback_sql(expression)
    3617
    3618        case = exp.Case().when(
    3619            expression.this.is_(exp.null()).not_(copy=False),
    3620            expression.args["true"],
    3621            copy=False,
    3622        )
    3623        else_cond = expression.args.get("false")
    3624        if else_cond:
    3625            case.else_(else_cond, copy=False)
    3626
    3627        return self.sql(case)

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

    3629    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3630        this = self.sql(expression, "this")
    3631        expr = self.sql(expression, "expression")
    3632        iterator = self.sql(expression, "iterator")
    3633        condition = self.sql(expression, "condition")
    3634        condition = f" IF {condition}" if condition else ""
    3635        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3643    def predict_sql(self, expression: exp.Predict) -> str:
    3644        model = self.sql(expression, "this")
    3645        model = f"MODEL {model}"
    3646        table = self.sql(expression, "expression")
    3647        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3648        parameters = self.sql(expression, "params_struct")
    3649        return self.func("PREDICT", model, table, parameters or None)

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

    3651    def forin_sql(self, expression: exp.ForIn) -> str:
    3652        this = self.sql(expression, "this")
    3653        expression_sql = self.sql(expression, "expression")
    3654        return f"FOR {this} DO {expression_sql}"

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

    3656    def refresh_sql(self, expression: exp.Refresh) -> str:
    3657        this = self.sql(expression, "this")
    3658        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3659        return f"REFRESH {table}{this}"

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

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

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

    3664    def toarray_sql(self, expression: exp.ToArray) -> str:
    3665        arg = expression.this
    3666        if not arg.type:
    3667            from sqlglot.optimizer.annotate_types import annotate_types
    3668
    3669            arg = annotate_types(arg)
    3670
    3671        if arg.is_type(exp.DataType.Type.ARRAY):
    3672            return self.sql(arg)
    3673
    3674        cond_for_null = arg.is_(exp.null())
    3675        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

    3677    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3678        this = expression.this
    3679        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3680            return self.sql(this)
    3681
    3682        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

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

    3684    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3685        this = expression.this
    3686        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3687            return self.sql(this)
    3688
    3689        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP))

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

    3691    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3692        this = expression.this
    3693        time_format = self.format_time(expression)
    3694
    3695        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3696            return self.sql(
    3697                exp.cast(
    3698                    exp.StrToTime(this=this, format=expression.args["format"]),
    3699                    exp.DataType.Type.DATE,
    3700                )
    3701            )
    3702
    3703        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3704            return self.sql(this)
    3705
    3706        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

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

    3708    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3709        return self.sql(
    3710            exp.func(
    3711                "DATEDIFF",
    3712                expression.this,
    3713                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    3714                "day",
    3715            )
    3716        )

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

    3718    def lastday_sql(self, expression: exp.LastDay) -> str:
    3719        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3720            return self.function_fallback_sql(expression)
    3721
    3722        unit = expression.text("unit")
    3723        if unit and unit != "MONTH":
    3724            self.unsupported("Date parts are not supported in LAST_DAY.")
    3725
    3726        return self.func("LAST_DAY", expression.this)

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

    3728    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    3729        from sqlglot.dialects.dialect import unit_to_str
    3730
    3731        return self.func(
    3732            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    3733        )

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

    3735    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3736        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3737            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3738            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3739            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3740            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3741
    3742        from sqlglot.dialects import Dialect
    3743
    3744        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3745        if self.dialect.__class__ != Dialect:
    3746            self.unsupported("ARRAY_ANY is unsupported")
    3747
    3748        return self.function_fallback_sql(expression)

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

    3750    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3751        expression.set("is_end_exclusive", None)
    3752        return self.function_fallback_sql(expression)

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

    3754    def struct_sql(self, expression: exp.Struct) -> str:
    3755        expression.set(
    3756            "expressions",
    3757            [
    3758                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    3759                if isinstance(e, exp.PropertyEQ)
    3760                else e
    3761                for e in expression.expressions
    3762            ],
    3763        )
    3764
    3765        return self.function_fallback_sql(expression)

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

    3767    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3768        low = self.sql(expression, "this")
    3769        high = self.sql(expression, "expression")
    3770
    3771        return f"{low} TO {high}"

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

    3773    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3774        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3775        tables = f" {self.expressions(expression)}"
    3776
    3777        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3778
    3779        on_cluster = self.sql(expression, "cluster")
    3780        on_cluster = f" {on_cluster}" if on_cluster else ""
    3781
    3782        identity = self.sql(expression, "identity")
    3783        identity = f" {identity} IDENTITY" if identity else ""
    3784
    3785        option = self.sql(expression, "option")
    3786        option = f" {option}" if option else ""
    3787
    3788        partition = self.sql(expression, "partition")
    3789        partition = f" {partition}" if partition else ""
    3790
    3791        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

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

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

    3891    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    3892        option = self.sql(expression, "this")
    3893
    3894        if expression.expressions:
    3895            upper = option.upper()
    3896
    3897            # Snowflake FILE_FORMAT options are separated by whitespace
    3898            sep = " " if upper == "FILE_FORMAT" else ", "
    3899
    3900            # Databricks copy/format options do not set their list of values with EQ
    3901            op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = "
    3902            values = self.expressions(expression, flat=True, sep=sep)
    3903            return f"{option}{op}({values})"
    3904
    3905        value = self.sql(expression, "expression")
    3906
    3907        if not value:
    3908            return option
    3909
    3910        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    3911
    3912        return f"{option}{op}{value}"

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

    3914    def credentials_sql(self, expression: exp.Credentials) -> str:
    3915        cred_expr = expression.args.get("credentials")
    3916        if isinstance(cred_expr, exp.Literal):
    3917            # Redshift case: CREDENTIALS <string>
    3918            credentials = self.sql(expression, "credentials")
    3919            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    3920        else:
    3921            # Snowflake case: CREDENTIALS = (...)
    3922            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    3923            credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else ""
    3924
    3925        storage = self.sql(expression, "storage")
    3926        storage = f"STORAGE_INTEGRATION = {storage}" if storage else ""
    3927
    3928        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    3929        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    3930
    3931        iam_role = self.sql(expression, "iam_role")
    3932        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    3933
    3934        region = self.sql(expression, "region")
    3935        region = f" REGION {region}" if region else ""
    3936
    3937        return f"{credentials}{storage}{encryption}{iam_role}{region}"

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

    3939    def copy_sql(self, expression: exp.Copy) -> str:
    3940        this = self.sql(expression, "this")
    3941        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    3942
    3943        credentials = self.sql(expression, "credentials")
    3944        credentials = self.seg(credentials) if credentials else ""
    3945        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    3946        files = self.expressions(expression, key="files", flat=True)
    3947
    3948        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    3949        params = self.expressions(
    3950            expression,
    3951            key="params",
    3952            sep=sep,
    3953            new_line=True,
    3954            skip_last=True,
    3955            skip_first=True,
    3956            indent=self.COPY_PARAMS_ARE_WRAPPED,
    3957        )
    3958
    3959        if params:
    3960            if self.COPY_PARAMS_ARE_WRAPPED:
    3961                params = f" WITH ({params})"
    3962            elif not self.pretty:
    3963                params = f" {params}"
    3964
    3965        return f"COPY{this}{kind} {files}{credentials}{params}"

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

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

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

    3970    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    3971        on_sql = "ON" if expression.args.get("on") else "OFF"
    3972        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    3973        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    3974        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    3975        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    3976
    3977        if filter_col or retention_period:
    3978            on_sql = self.func("ON", filter_col, retention_period)
    3979
    3980        return f"DATA_DELETION={on_sql}"

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

    3982    def maskingpolicycolumnconstraint_sql(
    3983        self, expression: exp.MaskingPolicyColumnConstraint
    3984    ) -> str:
    3985        this = self.sql(expression, "this")
    3986        expressions = self.expressions(expression, flat=True)
    3987        expressions = f" USING ({expressions})" if expressions else ""
    3988        return f"MASKING POLICY {this}{expressions}"

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

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

def scope_resolution(self, rhs: str, scope_name: str) -> str: View Source

    3995    def scope_resolution(self, rhs: str, scope_name: str) -> str:
    3996        return self.func("SCOPE_RESOLUTION", scope_name or None, rhs)

def scoperesolution_sql(self, expression: sqlglot.expressions.ScopeResolution) -> str: View Source

    3998    def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str:
    3999        this = self.sql(expression, "this")
    4000        expr = expression.expression
    4001
    4002        if isinstance(expr, exp.Func):
    4003            # T-SQL's CLR functions are case sensitive
    4004            expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})"
    4005        else:
    4006            expr = self.sql(expression, "expression")
    4007
    4008        return self.scope_resolution(expr, this)

def parsejson_sql(self, expression: sqlglot.expressions.ParseJSON) -> str: View Source

    4010    def parsejson_sql(self, expression: exp.ParseJSON) -> str:
    4011        if self.PARSE_JSON_NAME is None:
    4012            return self.sql(expression.this)
    4013
    4014        return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression)

def length_sql(self, expression: sqlglot.expressions.Length) -> str: View Source

    4016    def length_sql(self, expression: exp.Length) -> str:
    4017        return self.func("LENGTH", expression.this)

def rand_sql(self, expression: sqlglot.expressions.Rand) -> str: View Source

    4019    def rand_sql(self, expression: exp.Rand) -> str:
    4020        lower = self.sql(expression, "lower")
    4021        upper = self.sql(expression, "upper")
    4022
    4023        if lower and upper:
    4024            return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}"
    4025        return self.func("RAND", expression.this)

def strtodate_sql(self, expression: sqlglot.expressions.StrToDate) -> str: View Source

    4027    def strtodate_sql(self, expression: exp.StrToDate) -> str:
    4028        return self.func("STR_TO_DATE", expression.this, expression.args.get("format"))

def strtotime_sql(self, expression: sqlglot.expressions.StrToTime) -> str: View Source

    4030    def strtotime_sql(self, expression: exp.StrToTime) -> str:
    4031        return self.func(
    4032            "STR_TO_TIME",
    4033            expression.this,
    4034            expression.args.get("format"),
    4035            expression.args.get("zone"),
    4036        )

def changes_sql(self, expression: sqlglot.expressions.Changes) -> str: View Source

    4038    def changes_sql(self, expression: exp.Changes) -> str:
    4039        information = self.sql(expression, "information")
    4040        information = f"INFORMATION => {information}"
    4041        at_before = self.sql(expression, "at_before")
    4042        at_before = f"{self.seg('')}{at_before}" if at_before else ""
    4043        end = self.sql(expression, "end")
    4044        end = f"{self.seg('')}{end}" if end else ""
    4045
    4046        return f"CHANGES ({information}){at_before}{end}"