Edit on GitHub

# sqlglot.generator

View Source

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

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

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

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

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

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

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

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

Apply generic preprocessing transformations to a given expression.

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

    658    def unsupported(self, message: str) -> None:
    659        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    660            raise UnsupportedError(message)
    661        self.unsupported_messages.append(message)

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

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

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

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

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

    669    def pad_comment(self, comment: str) -> str:
    670        comment = " " + comment if comment[0].strip() else comment
    671        comment = comment + " " if comment[-1].strip() else comment
    672        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

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

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

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

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

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

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

    727    def normalize_func(self, name: str) -> str:
    728        if self.normalize_functions == "upper" or self.normalize_functions is True:
    729            return name.upper()
    730        if self.normalize_functions == "lower":
    731            return name.lower()
    732        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    924    def periodforsystemtimeconstraint_sql(
    925        self, expression: exp.PeriodForSystemTimeConstraint
    926    ) -> str:
    927        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    1372    def root_properties(self, properties: exp.Properties) -> str:
    1373        if properties.expressions:
    1374            return self.expressions(properties, indent=False, sep=" ")
    1375        return ""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2105    def null_sql(self, *_) -> str:
    2106        return "NULL"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2360    def parameter_sql(self, expression: exp.Parameter) -> str:
    2361        this = self.sql(expression, "this")
    2362        return f"{self.PARAMETER_TOKEN}{this}"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2578    def trim_sql(self, expression: exp.Trim) -> str:
    2579        trim_type = self.sql(expression, "position")
    2580
    2581        if trim_type == "LEADING":
    2582            return self.func("LTRIM", expression.this)
    2583        elif trim_type == "TRAILING":
    2584            return self.func("RTRIM", expression.this)
    2585        else:
    2586            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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2872    def and_sql(
    2873        self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None
    2874    ) -> str:
    2875        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

    2877    def or_sql(
    2878        self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None
    2879    ) -> str:
    2880        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

    2882    def xor_sql(
    2883        self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None
    2884    ) -> str:
    2885        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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    2941    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2942        format_sql = self.sql(expression, "format")
    2943        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2944        to_sql = self.sql(expression, "to")
    2945        to_sql = f" {to_sql}" if to_sql else ""
    2946        action = self.sql(expression, "action")
    2947        action = f" {action}" if action else ""
    2948        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

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

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

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

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

    2957    def collate_sql(self, expression: exp.Collate) -> str:
    2958        if self.COLLATE_IS_FUNC:
    2959            return self.function_fallback_sql(expression)
    2960        return self.binary(expression, "COLLATE")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3010    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    3011        this = self.sql(expression, "this")
    3012
    3013        dtype = self.sql(expression, "dtype")
    3014        if dtype:
    3015            collate = self.sql(expression, "collate")
    3016            collate = f" COLLATE {collate}" if collate else ""
    3017            using = self.sql(expression, "using")
    3018            using = f" USING {using}" if using else ""
    3019            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    3020
    3021        default = self.sql(expression, "default")
    3022        if default:
    3023            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    3024
    3025        comment = self.sql(expression, "comment")
    3026        if comment:
    3027            return f"ALTER COLUMN {this} COMMENT {comment}"
    3028
    3029        allow_null = expression.args.get("allow_null")
    3030        drop = expression.args.get("drop")
    3031
    3032        if not drop and not allow_null:
    3033            self.unsupported("Unsupported ALTER COLUMN syntax")
    3034
    3035        if allow_null is not None:
    3036            keyword = "DROP" if drop else "SET"
    3037            return f"ALTER COLUMN {this} {keyword} NOT NULL"
    3038
    3039        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    3041    def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str:
    3042        this = self.sql(expression, "this")
    3043        if not isinstance(expression.this, exp.Var):
    3044            this = f"KEY DISTKEY {this}"
    3045        return f"ALTER DISTSTYLE {this}"

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

    3047    def altersortkey_sql(self, expression: exp.AlterSortKey) -> str:
    3048        compound = " COMPOUND" if expression.args.get("compound") else ""
    3049        this = self.sql(expression, "this")
    3050        expressions = self.expressions(expression, flat=True)
    3051        expressions = f"({expressions})" if expressions else ""
    3052        return f"ALTER{compound} SORTKEY {this or expressions}"

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

    3054    def renametable_sql(self, expression: exp.RenameTable) -> str:
    3055        if not self.RENAME_TABLE_WITH_DB:
    3056            # Remove db from tables
    3057            expression = expression.transform(
    3058                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    3059            ).assert_is(exp.RenameTable)
    3060        this = self.sql(expression, "this")
    3061        return f"RENAME TO {this}"

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

    3063    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    3064        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3065        old_column = self.sql(expression, "this")
    3066        new_column = self.sql(expression, "to")
    3067        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

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

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

    3073    def altertable_sql(self, expression: exp.AlterTable) -> str:
    3074        actions = expression.args["actions"]
    3075
    3076        if isinstance(actions[0], exp.ColumnDef):
    3077            actions = self.add_column_sql(expression)
    3078        elif isinstance(actions[0], exp.Schema):
    3079            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    3080        elif isinstance(actions[0], exp.Delete):
    3081            actions = self.expressions(expression, key="actions", flat=True)
    3082        else:
    3083            actions = self.expressions(expression, key="actions", flat=True)
    3084
    3085        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3086        on_cluster = self.sql(expression, "cluster")
    3087        on_cluster = f" {on_cluster}" if on_cluster else ""
    3088        only = " ONLY" if expression.args.get("only") else ""
    3089        options = self.expressions(expression, key="options")
    3090        options = f", {options}" if options else ""
    3091        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

    3093    def add_column_sql(self, expression: exp.AlterTable) -> str:
    3094        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    3095            return self.expressions(
    3096                expression,
    3097                key="actions",
    3098                prefix="ADD COLUMN ",
    3099            )
    3100        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    3102    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    3103        expressions = self.expressions(expression)
    3104        exists = " IF EXISTS " if expression.args.get("exists") else " "
    3105        return f"DROP{exists}{expressions}"

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

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

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

    3110    def distinct_sql(self, expression: exp.Distinct) -> str:
    3111        this = self.expressions(expression, flat=True)
    3112
    3113        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    3114            case = exp.case()
    3115            for arg in expression.expressions:
    3116                case = case.when(arg.is_(exp.null()), exp.null())
    3117            this = self.sql(case.else_(f"({this})"))
    3118
    3119        this = f" {this}" if this else ""
    3120
    3121        on = self.sql(expression, "on")
    3122        on = f" ON {on}" if on else ""
    3123        return f"DISTINCT{this}{on}"

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

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

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

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

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

    3131    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    3132        this_sql = self.sql(expression, "this")
    3133        expression_sql = self.sql(expression, "expression")
    3134        kind = "MAX" if expression.args.get("max") else "MIN"
    3135        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    3137    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    3138        return self.sql(
    3139            exp.Cast(
    3140                this=exp.Div(this=expression.this, expression=expression.expression),
    3141                to=exp.DataType(this=exp.DataType.Type.INT),
    3142            )
    3143        )

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

    3145    def dpipe_sql(self, expression: exp.DPipe) -> str:
    3146        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    3147            return self.func(
    3148                "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten())
    3149            )
    3150        return self.binary(expression, "||")

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

    3152    def div_sql(self, expression: exp.Div) -> str:
    3153        l, r = expression.left, expression.right
    3154
    3155        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    3156            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    3157
    3158        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    3159            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    3160                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    3161
    3162        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    3163            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    3164                return self.sql(
    3165                    exp.cast(
    3166                        l / r,
    3167                        to=exp.DataType.Type.BIGINT,
    3168                    )
    3169                )
    3170
    3171        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3206    def is_sql(self, expression: exp.Is) -> str:
    3207        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3208            return self.sql(
    3209                expression.this if expression.expression.this else exp.not_(expression.this)
    3210            )
    3211        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3252    def try_sql(self, expression: exp.Try) -> str:
    3253        if not self.TRY_SUPPORTED:
    3254            self.unsupported("Unsupported TRY function")
    3255            return self.sql(expression, "this")
    3256
    3257        return self.func("TRY", expression.this)

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

    3259    def log_sql(self, expression: exp.Log) -> str:
    3260        this = expression.this
    3261        expr = expression.expression
    3262
    3263        if self.dialect.LOG_BASE_FIRST is False:
    3264            this, expr = expr, this
    3265        elif self.dialect.LOG_BASE_FIRST is None and expr:
    3266            if this.name in ("2", "10"):
    3267                return self.func(f"LOG{this.name}", expr)
    3268
    3269            self.unsupported(f"Unsupported logarithm with base {self.sql(this)}")
    3270
    3271        return self.func("LOG", this, expr)

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

    3273    def use_sql(self, expression: exp.Use) -> str:
    3274        kind = self.sql(expression, "kind")
    3275        kind = f" {kind}" if kind else ""
    3276        this = self.sql(expression, "this")
    3277        this = f" {this}" if this else ""
    3278        return f"USE{kind}{this}"

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

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

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

    3284    def function_fallback_sql(self, expression: exp.Func) -> str:
    3285        args = []
    3286
    3287        for key in expression.arg_types:
    3288            arg_value = expression.args.get(key)
    3289
    3290            if isinstance(arg_value, list):
    3291                for value in arg_value:
    3292                    args.append(value)
    3293            elif arg_value is not None:
    3294                args.append(arg_value)
    3295
    3296        if self.normalize_functions:
    3297            name = expression.sql_name()
    3298        else:
    3299            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3300
    3301        return self.func(name, *args)

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

    3303    def func(
    3304        self,
    3305        name: str,
    3306        *args: t.Optional[exp.Expression | str],
    3307        prefix: str = "(",
    3308        suffix: str = ")",
    3309    ) -> str:
    3310        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

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

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

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

    3321    def format_time(
    3322        self,
    3323        expression: exp.Expression,
    3324        inverse_time_mapping: t.Optional[t.Dict[str, str]] = None,
    3325        inverse_time_trie: t.Optional[t.Dict] = None,
    3326    ) -> t.Optional[str]:
    3327        return format_time(
    3328            self.sql(expression, "format"),
    3329            inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING,
    3330            inverse_time_trie or self.dialect.INVERSE_TIME_TRIE,
    3331        )

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

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

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

    3392    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3393        flat = flat or isinstance(expression.parent, exp.Properties)
    3394        expressions_sql = self.expressions(expression, flat=flat)
    3395        if flat:
    3396            return f"{op} {expressions_sql}"
    3397        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3399    def naked_property(self, expression: exp.Property) -> str:
    3400        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3401        if not property_name:
    3402            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3403        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3411    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3412        this = self.sql(expression, "this")
    3413        expressions = self.no_identify(self.expressions, expression)
    3414        expressions = (
    3415            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3416        )
    3417        return f"{this}{expressions}"

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

    3419    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3420        this = self.sql(expression, "this")
    3421        expressions = self.expressions(expression, flat=True)
    3422        return f"{this}({expressions})"

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

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

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

    3427    def when_sql(self, expression: exp.When) -> str:
    3428        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3429        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3430        condition = self.sql(expression, "condition")
    3431        condition = f" AND {condition}" if condition else ""
    3432
    3433        then_expression = expression.args.get("then")
    3434        if isinstance(then_expression, exp.Insert):
    3435            this = self.sql(then_expression, "this")
    3436            this = f"INSERT {this}" if this else "INSERT"
    3437            then = self.sql(then_expression, "expression")
    3438            then = f"{this} VALUES {then}" if then else this
    3439        elif isinstance(then_expression, exp.Update):
    3440            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3441                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3442            else:
    3443                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3444        else:
    3445            then = self.sql(then_expression)
    3446        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3448    def merge_sql(self, expression: exp.Merge) -> str:
    3449        table = expression.this
    3450        table_alias = ""
    3451
    3452        hints = table.args.get("hints")
    3453        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3454            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3455            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3456
    3457        this = self.sql(table)
    3458        using = f"USING {self.sql(expression, 'using')}"
    3459        on = f"ON {self.sql(expression, 'on')}"
    3460        expressions = self.expressions(expression, sep=" ", indent=False)
    3461        sep = self.sep()
    3462
    3463        return self.prepend_ctes(
    3464            expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}"
    3465        )

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

    3467    def tochar_sql(self, expression: exp.ToChar) -> str:
    3468        if expression.args.get("format"):
    3469            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3470
    3471        return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT))

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

    3473    def tonumber_sql(self, expression: exp.ToNumber) -> str:
    3474        if not self.SUPPORTS_TO_NUMBER:
    3475            self.unsupported("Unsupported TO_NUMBER function")
    3476            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3477
    3478        fmt = expression.args.get("format")
    3479        if not fmt:
    3480            self.unsupported("Conversion format is required for TO_NUMBER")
    3481            return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE))
    3482
    3483        return self.func("TO_NUMBER", expression.this, fmt)

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

    3485    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3486        this = self.sql(expression, "this")
    3487        kind = self.sql(expression, "kind")
    3488        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3489        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3490        return f"{this}({kind}{args})"

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

    3492    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3493        this = self.sql(expression, "this")
    3494        max = self.sql(expression, "max")
    3495        min = self.sql(expression, "min")
    3496        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3504    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3505        expressions = self.expressions(expression, key="expressions", flat=True)
    3506        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3507        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3508        buckets = self.sql(expression, "buckets")
    3509        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3511    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3512        this = self.sql(expression, "this")
    3513        having = self.sql(expression, "having")
    3514
    3515        if having:
    3516            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3517
    3518        return self.func("ANY_VALUE", this)

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

    3520    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3521        transform = self.func("TRANSFORM", *expression.expressions)
    3522        row_format_before = self.sql(expression, "row_format_before")
    3523        row_format_before = f" {row_format_before}" if row_format_before else ""
    3524        record_writer = self.sql(expression, "record_writer")
    3525        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3526        using = f" USING {self.sql(expression, 'command_script')}"
    3527        schema = self.sql(expression, "schema")
    3528        schema = f" AS {schema}" if schema else ""
    3529        row_format_after = self.sql(expression, "row_format_after")
    3530        row_format_after = f" {row_format_after}" if row_format_after else ""
    3531        record_reader = self.sql(expression, "record_reader")
    3532        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3533        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

    3535    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3536        key_block_size = self.sql(expression, "key_block_size")
    3537        if key_block_size:
    3538            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3539
    3540        using = self.sql(expression, "using")
    3541        if using:
    3542            return f"USING {using}"
    3543
    3544        parser = self.sql(expression, "parser")
    3545        if parser:
    3546            return f"WITH PARSER {parser}"
    3547
    3548        comment = self.sql(expression, "comment")
    3549        if comment:
    3550            return f"COMMENT {comment}"
    3551
    3552        visible = expression.args.get("visible")
    3553        if visible is not None:
    3554            return "VISIBLE" if visible else "INVISIBLE"
    3555
    3556        engine_attr = self.sql(expression, "engine_attr")
    3557        if engine_attr:
    3558            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3559
    3560        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3561        if secondary_engine_attr:
    3562            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3563
    3564        self.unsupported("Unsupported index constraint option.")
    3565        return ""

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

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

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

    3571    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3572        kind = self.sql(expression, "kind")
    3573        kind = f"{kind} INDEX" if kind else "INDEX"
    3574        this = self.sql(expression, "this")
    3575        this = f" {this}" if this else ""
    3576        index_type = self.sql(expression, "index_type")
    3577        index_type = f" USING {index_type}" if index_type else ""
    3578        expressions = self.expressions(expression, flat=True)
    3579        expressions = f" ({expressions})" if expressions else ""
    3580        options = self.expressions(expression, key="options", sep=" ")
    3581        options = f" {options}" if options else ""
    3582        return f"{kind}{this}{index_type}{expressions}{options}"

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

    3584    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3585        if self.NVL2_SUPPORTED:
    3586            return self.function_fallback_sql(expression)
    3587
    3588        case = exp.Case().when(
    3589            expression.this.is_(exp.null()).not_(copy=False),
    3590            expression.args["true"],
    3591            copy=False,
    3592        )
    3593        else_cond = expression.args.get("false")
    3594        if else_cond:
    3595            case.else_(else_cond, copy=False)
    3596
    3597        return self.sql(case)

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

    3599    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3600        this = self.sql(expression, "this")
    3601        expr = self.sql(expression, "expression")
    3602        iterator = self.sql(expression, "iterator")
    3603        condition = self.sql(expression, "condition")
    3604        condition = f" IF {condition}" if condition else ""
    3605        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3613    def predict_sql(self, expression: exp.Predict) -> str:
    3614        model = self.sql(expression, "this")
    3615        model = f"MODEL {model}"
    3616        table = self.sql(expression, "expression")
    3617        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3618        parameters = self.sql(expression, "params_struct")
    3619        return self.func("PREDICT", model, table, parameters or None)

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

    3621    def forin_sql(self, expression: exp.ForIn) -> str:
    3622        this = self.sql(expression, "this")
    3623        expression_sql = self.sql(expression, "expression")
    3624        return f"FOR {this} DO {expression_sql}"

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

    3626    def refresh_sql(self, expression: exp.Refresh) -> str:
    3627        this = self.sql(expression, "this")
    3628        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3629        return f"REFRESH {table}{this}"

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

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

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

    3634    def toarray_sql(self, expression: exp.ToArray) -> str:
    3635        arg = expression.this
    3636        if not arg.type:
    3637            from sqlglot.optimizer.annotate_types import annotate_types
    3638
    3639            arg = annotate_types(arg)
    3640
    3641        if arg.is_type(exp.DataType.Type.ARRAY):
    3642            return self.sql(arg)
    3643
    3644        cond_for_null = arg.is_(exp.null())
    3645        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

    3647    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3648        this = expression.this
    3649        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3650            return self.sql(this)
    3651
    3652        return self.sql(exp.cast(this, exp.DataType.Type.TIME))

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

    3654    def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str:
    3655        this = expression.this
    3656        if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP):
    3657            return self.sql(this)
    3658
    3659        return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP))

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

    3661    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3662        this = expression.this
    3663        time_format = self.format_time(expression)
    3664
    3665        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3666            return self.sql(
    3667                exp.cast(
    3668                    exp.StrToTime(this=this, format=expression.args["format"]),
    3669                    exp.DataType.Type.DATE,
    3670                )
    3671            )
    3672
    3673        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3674            return self.sql(this)
    3675
    3676        return self.sql(exp.cast(this, exp.DataType.Type.DATE))

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

    3678    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3679        return self.sql(
    3680            exp.func(
    3681                "DATEDIFF",
    3682                expression.this,
    3683                exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE),
    3684                "day",
    3685            )
    3686        )

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

    3688    def lastday_sql(self, expression: exp.LastDay) -> str:
    3689        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3690            return self.function_fallback_sql(expression)
    3691
    3692        unit = expression.text("unit")
    3693        if unit and unit != "MONTH":
    3694            self.unsupported("Date parts are not supported in LAST_DAY.")
    3695
    3696        return self.func("LAST_DAY", expression.this)

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

    3698    def dateadd_sql(self, expression: exp.DateAdd) -> str:
    3699        from sqlglot.dialects.dialect import unit_to_str
    3700
    3701        return self.func(
    3702            "DATE_ADD", expression.this, expression.expression, unit_to_str(expression)
    3703        )

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

    3705    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3706        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3707            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3708            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3709            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3710            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3711
    3712        from sqlglot.dialects import Dialect
    3713
    3714        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3715        if self.dialect.__class__ != Dialect:
    3716            self.unsupported("ARRAY_ANY is unsupported")
    3717
    3718        return self.function_fallback_sql(expression)

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

    3720    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3721        expression.set("is_end_exclusive", None)
    3722        return self.function_fallback_sql(expression)

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

    3724    def struct_sql(self, expression: exp.Struct) -> str:
    3725        expression.set(
    3726            "expressions",
    3727            [
    3728                exp.alias_(e.expression, e.name if e.this.is_string else e.this)
    3729                if isinstance(e, exp.PropertyEQ)
    3730                else e
    3731                for e in expression.expressions
    3732            ],
    3733        )
    3734
    3735        return self.function_fallback_sql(expression)

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

    3737    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3738        low = self.sql(expression, "this")
    3739        high = self.sql(expression, "expression")
    3740
    3741        return f"{low} TO {high}"

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

    3743    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3744        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3745        tables = f" {self.expressions(expression)}"
    3746
    3747        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3748
    3749        on_cluster = self.sql(expression, "cluster")
    3750        on_cluster = f" {on_cluster}" if on_cluster else ""
    3751
    3752        identity = self.sql(expression, "identity")
    3753        identity = f" {identity} IDENTITY" if identity else ""
    3754
    3755        option = self.sql(expression, "option")
    3756        option = f" {option}" if option else ""
    3757
    3758        partition = self.sql(expression, "partition")
    3759        partition = f" {partition}" if partition else ""
    3760
    3761        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

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

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

    3861    def copyparameter_sql(self, expression: exp.CopyParameter) -> str:
    3862        option = self.sql(expression, "this")
    3863
    3864        if option.upper() == "FILE_FORMAT":
    3865            values = self.expressions(expression, key="expression", flat=True, sep=" ")
    3866            return f"{option} = ({values})"
    3867
    3868        value = self.sql(expression, "expression")
    3869
    3870        if not value:
    3871            return option
    3872
    3873        op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " "
    3874
    3875        return f"{option}{op}{value}"

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

    3877    def credentials_sql(self, expression: exp.Credentials) -> str:
    3878        cred_expr = expression.args.get("credentials")
    3879        if isinstance(cred_expr, exp.Literal):
    3880            # Redshift case: CREDENTIALS <string>
    3881            credentials = self.sql(expression, "credentials")
    3882            credentials = f"CREDENTIALS {credentials}" if credentials else ""
    3883        else:
    3884            # Snowflake case: CREDENTIALS = (...)
    3885            credentials = self.expressions(expression, key="credentials", flat=True, sep=" ")
    3886            credentials = f"CREDENTIALS = ({credentials})" if credentials else ""
    3887
    3888        storage = self.sql(expression, "storage")
    3889
    3890        encryption = self.expressions(expression, key="encryption", flat=True, sep=" ")
    3891        encryption = f" ENCRYPTION = ({encryption})" if encryption else ""
    3892
    3893        iam_role = self.sql(expression, "iam_role")
    3894        iam_role = f"IAM_ROLE {iam_role}" if iam_role else ""
    3895
    3896        region = self.sql(expression, "region")
    3897        region = f" REGION {region}" if region else ""
    3898
    3899        return f"{credentials}{storage}{encryption}{iam_role}{region}"

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

    3901    def copy_sql(self, expression: exp.Copy) -> str:
    3902        this = self.sql(expression, "this")
    3903        this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}"
    3904
    3905        credentials = self.sql(expression, "credentials")
    3906        credentials = self.seg(credentials) if credentials else ""
    3907        kind = self.seg("FROM" if expression.args.get("kind") else "TO")
    3908        files = self.expressions(expression, key="files", flat=True)
    3909
    3910        sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " "
    3911        params = self.expressions(
    3912            expression,
    3913            key="params",
    3914            sep=sep,
    3915            new_line=True,
    3916            skip_last=True,
    3917            skip_first=True,
    3918            indent=self.COPY_PARAMS_ARE_WRAPPED,
    3919        )
    3920
    3921        if params:
    3922            if self.COPY_PARAMS_ARE_WRAPPED:
    3923                params = f" WITH ({params})"
    3924            elif not self.pretty:
    3925                params = f" {params}"
    3926
    3927        return f"COPY{this}{kind} {files}{credentials}{params}"

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

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

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

    3932    def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str:
    3933        on_sql = "ON" if expression.args.get("on") else "OFF"
    3934        filter_col: t.Optional[str] = self.sql(expression, "filter_column")
    3935        filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None
    3936        retention_period: t.Optional[str] = self.sql(expression, "retention_period")
    3937        retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None
    3938
    3939        if filter_col or retention_period:
    3940            on_sql = self.func("ON", filter_col, retention_period)
    3941
    3942        return f"DATA_DELETION={on_sql}"

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

    3944    def maskingpolicycolumnconstraint_sql(
    3945        self, expression: exp.MaskingPolicyColumnConstraint
    3946    ) -> str:
    3947        this = self.sql(expression, "this")
    3948        expressions = self.expressions(expression, flat=True)
    3949        expressions = f" USING ({expressions})" if expressions else ""
    3950        return f"MASKING POLICY {this}{expressions}"