sqlglot.generator API documentation

Edit on GitHub

# sqlglot.generator

View Source

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

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

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. Default: 2.
- indent: The indentation size in a formatted string. 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

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

TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] = {<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.DateAdd'>: <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.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.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
NULL_ORDERING_SUPPORTED: Optional[bool] = True
IGNORE_NULLS_IN_FUNC = False
LOCKING_READS_SUPPORTED = False
EXPLICIT_UNION = False
WRAP_DERIVED_VALUES = True
CREATE_FUNCTION_RETURN_AS = True
MATCHED_BY_SOURCE = True
SINGLE_STRING_INTERVAL = False
INTERVAL_ALLOWS_PLURAL_FORM = True
LIMIT_FETCH = 'ALL'
LIMIT_ONLY_LITERALS = False
RENAME_TABLE_WITH_DB = True
GROUPINGS_SEP = ','
INDEX_ON = 'ON'
JOIN_HINTS = True
TABLE_HINTS = True
QUERY_HINTS = True
QUERY_HINT_SEP = ', '
IS_BOOL_ALLOWED = True
DUPLICATE_KEY_UPDATE_WITH_SET = True
LIMIT_IS_TOP = False
RETURNING_END = True
COLUMN_JOIN_MARKS_SUPPORTED = False
EXTRACT_ALLOWS_QUOTES = True
TZ_TO_WITH_TIME_ZONE = False
NVL2_SUPPORTED = True
SELECT_KINDS: Tuple[str, ...] = ('STRUCT', 'VALUE')
VALUES_AS_TABLE = True
ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True
UNNEST_WITH_ORDINALITY = True
AGGREGATE_FILTER_SUPPORTED = True
SEMI_ANTI_JOIN_WITH_SIDE = True
COMPUTED_COLUMN_WITH_TYPE = True
SUPPORTS_TABLE_COPY = True
TABLESAMPLE_REQUIRES_PARENS = True
TABLESAMPLE_SIZE_IS_ROWS = True
TABLESAMPLE_KEYWORDS = 'TABLESAMPLE'
TABLESAMPLE_WITH_METHOD = True
TABLESAMPLE_SEED_KEYWORD = 'SEED'
COLLATE_IS_FUNC = False
DATA_TYPE_SPECIFIERS_ALLOWED = False
ENSURE_BOOLS = False
CTE_RECURSIVE_KEYWORD_REQUIRED = True
SUPPORTS_SINGLE_ARG_CONCAT = True
LAST_DAY_SUPPORTS_DATE_PART = True
SUPPORTS_TABLE_ALIAS_COLUMNS = True
UNPIVOT_ALIASES_ARE_IDENTIFIERS = True
JSON_KEY_VALUE_PAIR_SEP = ':'
INSERT_OVERWRITE = ' OVERWRITE TABLE'
SUPPORTS_SELECT_INTO = False
SUPPORTS_UNLOGGED_TABLES = False
SUPPORTS_CREATE_TABLE_LIKE = True
LIKE_PROPERTY_INSIDE_SCHEMA = False
MULTI_ARG_DISTINCT = True
JSON_TYPE_REQUIRED_FOR_EXTRACTION = False
JSON_PATH_BRACKETED_KEY_SUPPORTED = True
JSON_PATH_SINGLE_QUOTE_ESCAPE = False
SUPPORTED_JSON_PATH_PARTS = {<class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>}
CAN_IMPLEMENT_ARRAY_ANY = False
TYPE_MAPPING = {<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
STAR_MAPPING = {'except': 'EXCEPT', 'replace': 'REPLACE'}
TIME_PART_SINGULARS = {'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS = {'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
TOKEN_MAPPING: Dict[sqlglot.tokens.TokenType, str] = {}
STRUCT_DELIMITER = ('<', '>')
PARAMETER_TOKEN = '@'
NAMED_PLACEHOLDER_TOKEN = ':'
PROPERTIES_LOCATION = {<class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.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

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

    591    def preprocess(self, expression: exp.Expression) -> exp.Expression:
    592        """Apply generic preprocessing transformations to a given expression."""
    593        if (
    594            not expression.parent
    595            and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES
    596            and any(node.parent is not expression for node in expression.find_all(exp.With))
    597        ):
    598            from sqlglot.transforms import move_ctes_to_top_level
    599
    600            expression = move_ctes_to_top_level(expression)
    601
    602        if self.ENSURE_BOOLS:
    603            from sqlglot.transforms import ensure_bools
    604
    605            expression = ensure_bools(expression)
    606
    607        return expression

Apply generic preprocessing transformations to a given expression.

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

    609    def unsupported(self, message: str) -> None:
    610        if self.unsupported_level == ErrorLevel.IMMEDIATE:
    611            raise UnsupportedError(message)
    612        self.unsupported_messages.append(message)

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

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

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

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

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

    620    def pad_comment(self, comment: str) -> str:
    621        comment = " " + comment if comment[0].strip() else comment
    622        comment = comment + " " if comment[-1].strip() else comment
    623        return comment

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

    625    def maybe_comment(
    626        self,
    627        sql: str,
    628        expression: t.Optional[exp.Expression] = None,
    629        comments: t.Optional[t.List[str]] = None,
    630    ) -> str:
    631        comments = (
    632            ((expression and expression.comments) if comments is None else comments)  # type: ignore
    633            if self.comments
    634            else None
    635        )
    636
    637        if not comments or isinstance(expression, self.EXCLUDE_COMMENTS):
    638            return sql
    639
    640        comments_sql = " ".join(
    641            f"/*{self.pad_comment(comment)}*/" for comment in comments if comment
    642        )
    643
    644        if not comments_sql:
    645            return sql
    646
    647        if isinstance(expression, self.WITH_SEPARATED_COMMENTS):
    648            return (
    649                f"{self.sep()}{comments_sql}{sql}"
    650                if sql[0].isspace()
    651                else f"{comments_sql}{self.sep()}{sql}"
    652            )
    653
    654        return f"{sql} {comments_sql}"

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

    656    def wrap(self, expression: exp.Expression | str) -> str:
    657        this_sql = self.indent(
    658            (
    659                self.sql(expression)
    660                if isinstance(expression, exp.UNWRAPPED_QUERIES)
    661                else self.sql(expression, "this")
    662            ),
    663            level=1,
    664            pad=0,
    665        )
    666        return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"

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

    668    def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str:
    669        original = self.identify
    670        self.identify = False
    671        result = func(*args, **kwargs)
    672        self.identify = original
    673        return result

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

    675    def normalize_func(self, name: str) -> str:
    676        if self.normalize_functions == "upper" or self.normalize_functions is True:
    677            return name.upper()
    678        if self.normalize_functions == "lower":
    679            return name.lower()
    680        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

    682    def indent(
    683        self,
    684        sql: str,
    685        level: int = 0,
    686        pad: t.Optional[int] = None,
    687        skip_first: bool = False,
    688        skip_last: bool = False,
    689    ) -> str:
    690        if not self.pretty:
    691            return sql
    692
    693        pad = self.pad if pad is None else pad
    694        lines = sql.split("\n")
    695
    696        return "\n".join(
    697            (
    698                line
    699                if (skip_first and i == 0) or (skip_last and i == len(lines) - 1)
    700                else f"{' ' * (level * self._indent + pad)}{line}"
    701            )
    702            for i, line in enumerate(lines)
    703        )

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

    705    def sql(
    706        self,
    707        expression: t.Optional[str | exp.Expression],
    708        key: t.Optional[str] = None,
    709        comment: bool = True,
    710    ) -> str:
    711        if not expression:
    712            return ""
    713
    714        if isinstance(expression, str):
    715            return expression
    716
    717        if key:
    718            value = expression.args.get(key)
    719            if value:
    720                return self.sql(value)
    721            return ""
    722
    723        transform = self.TRANSFORMS.get(expression.__class__)
    724
    725        if callable(transform):
    726            sql = transform(self, expression)
    727        elif isinstance(expression, exp.Expression):
    728            exp_handler_name = f"{expression.key}_sql"
    729
    730            if hasattr(self, exp_handler_name):
    731                sql = getattr(self, exp_handler_name)(expression)
    732            elif isinstance(expression, exp.Func):
    733                sql = self.function_fallback_sql(expression)
    734            elif isinstance(expression, exp.Property):
    735                sql = self.property_sql(expression)
    736            else:
    737                raise ValueError(f"Unsupported expression type {expression.__class__.__name__}")
    738        else:
    739            raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
    740
    741        return self.maybe_comment(sql, expression) if self.comments and comment else sql

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

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

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

    748    def cache_sql(self, expression: exp.Cache) -> str:
    749        lazy = " LAZY" if expression.args.get("lazy") else ""
    750        table = self.sql(expression, "this")
    751        options = expression.args.get("options")
    752        options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else ""
    753        sql = self.sql(expression, "expression")
    754        sql = f" AS{self.sep()}{sql}" if sql else ""
    755        sql = f"CACHE{lazy} TABLE {table}{options}{sql}"
    756        return self.prepend_ctes(expression, sql)

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

    758    def characterset_sql(self, expression: exp.CharacterSet) -> str:
    759        if isinstance(expression.parent, exp.Cast):
    760            return f"CHAR CHARACTER SET {self.sql(expression, 'this')}"
    761        default = "DEFAULT " if expression.args.get("default") else ""
    762        return f"{default}CHARACTER SET={self.sql(expression, 'this')}"

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

    764    def column_sql(self, expression: exp.Column) -> str:
    765        join_mark = " (+)" if expression.args.get("join_mark") else ""
    766
    767        if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED:
    768            join_mark = ""
    769            self.unsupported("Outer join syntax using the (+) operator is not supported.")
    770
    771        column = ".".join(
    772            self.sql(part)
    773            for part in (
    774                expression.args.get("catalog"),
    775                expression.args.get("db"),
    776                expression.args.get("table"),
    777                expression.args.get("this"),
    778            )
    779            if part
    780        )
    781
    782        return f"{column}{join_mark}"

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

    784    def columnposition_sql(self, expression: exp.ColumnPosition) -> str:
    785        this = self.sql(expression, "this")
    786        this = f" {this}" if this else ""
    787        position = self.sql(expression, "position")
    788        return f"{position}{this}"

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

    790    def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str:
    791        column = self.sql(expression, "this")
    792        kind = self.sql(expression, "kind")
    793        constraints = self.expressions(expression, key="constraints", sep=" ", flat=True)
    794        exists = "IF NOT EXISTS " if expression.args.get("exists") else ""
    795        kind = f"{sep}{kind}" if kind else ""
    796        constraints = f" {constraints}" if constraints else ""
    797        position = self.sql(expression, "position")
    798        position = f" {position}" if position else ""
    799
    800        if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE:
    801            kind = ""
    802
    803        return f"{exists}{column}{kind}{constraints}{position}"

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

    805    def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str:
    806        this = self.sql(expression, "this")
    807        kind_sql = self.sql(expression, "kind").strip()
    808        return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql

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

    810    def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str:
    811        this = self.sql(expression, "this")
    812        if expression.args.get("not_null"):
    813            persisted = " PERSISTED NOT NULL"
    814        elif expression.args.get("persisted"):
    815            persisted = " PERSISTED"
    816        else:
    817            persisted = ""
    818        return f"AS {this}{persisted}"

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

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

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

    823    def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str:
    824        if isinstance(expression.this, list):
    825            this = self.wrap(self.expressions(expression, key="this", flat=True))
    826        else:
    827            this = self.sql(expression, "this")
    828
    829        return f"COMPRESS {this}"

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

    831    def generatedasidentitycolumnconstraint_sql(
    832        self, expression: exp.GeneratedAsIdentityColumnConstraint
    833    ) -> str:
    834        this = ""
    835        if expression.this is not None:
    836            on_null = " ON NULL" if expression.args.get("on_null") else ""
    837            this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}"
    838
    839        start = expression.args.get("start")
    840        start = f"START WITH {start}" if start else ""
    841        increment = expression.args.get("increment")
    842        increment = f" INCREMENT BY {increment}" if increment else ""
    843        minvalue = expression.args.get("minvalue")
    844        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
    845        maxvalue = expression.args.get("maxvalue")
    846        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
    847        cycle = expression.args.get("cycle")
    848        cycle_sql = ""
    849
    850        if cycle is not None:
    851            cycle_sql = f"{' NO' if not cycle else ''} CYCLE"
    852            cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql
    853
    854        sequence_opts = ""
    855        if start or increment or cycle_sql:
    856            sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}"
    857            sequence_opts = f" ({sequence_opts.strip()})"
    858
    859        expr = self.sql(expression, "expression")
    860        expr = f"({expr})" if expr else "IDENTITY"
    861
    862        return f"GENERATED{this} AS {expr}{sequence_opts}"

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

    864    def generatedasrowcolumnconstraint_sql(
    865        self, expression: exp.GeneratedAsRowColumnConstraint
    866    ) -> str:
    867        start = "START" if expression.args.get("start") else "END"
    868        hidden = " HIDDEN" if expression.args.get("hidden") else ""
    869        return f"GENERATED ALWAYS AS ROW {start}{hidden}"

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

    871    def periodforsystemtimeconstraint_sql(
    872        self, expression: exp.PeriodForSystemTimeConstraint
    873    ) -> str:
    874        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

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

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

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

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

    882    def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str:
    883        desc = expression.args.get("desc")
    884        if desc is not None:
    885            return f"PRIMARY KEY{' DESC' if desc else ' ASC'}"
    886        return "PRIMARY KEY"

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

    888    def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str:
    889        this = self.sql(expression, "this")
    890        this = f" {this}" if this else ""
    891        index_type = expression.args.get("index_type")
    892        index_type = f" USING {index_type}" if index_type else ""
    893        on_conflict = self.sql(expression, "on_conflict")
    894        on_conflict = f" {on_conflict}" if on_conflict else ""
    895        return f"UNIQUE{this}{index_type}{on_conflict}"

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

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

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

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

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

     987    def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str:
     988        start = self.sql(expression, "start")
     989        start = f"START WITH {start}" if start else ""
     990        increment = self.sql(expression, "increment")
     991        increment = f" INCREMENT BY {increment}" if increment else ""
     992        minvalue = self.sql(expression, "minvalue")
     993        minvalue = f" MINVALUE {minvalue}" if minvalue else ""
     994        maxvalue = self.sql(expression, "maxvalue")
     995        maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else ""
     996        owned = self.sql(expression, "owned")
     997        owned = f" OWNED BY {owned}" if owned else ""
     998
     999        cache = expression.args.get("cache")
    1000        if cache is None:
    1001            cache_str = ""
    1002        elif cache is True:
    1003            cache_str = " CACHE"
    1004        else:
    1005            cache_str = f" CACHE {cache}"
    1006
    1007        options = self.expressions(expression, key="options", flat=True, sep=" ")
    1008        options = f" {options}" if options else ""
    1009
    1010        return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()

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

    1012    def clone_sql(self, expression: exp.Clone) -> str:
    1013        this = self.sql(expression, "this")
    1014        shallow = "SHALLOW " if expression.args.get("shallow") else ""
    1015        keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE"
    1016        return f"{shallow}{keyword} {this}"

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

    1018    def describe_sql(self, expression: exp.Describe) -> str:
    1019        extended = " EXTENDED" if expression.args.get("extended") else ""
    1020        return f"DESCRIBE{extended} {self.sql(expression, 'this')}"

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

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

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

    1026    def prepend_ctes(self, expression: exp.Expression, sql: str) -> str:
    1027        with_ = self.sql(expression, "with")
    1028        if with_:
    1029            sql = f"{with_}{self.sep()}{sql}"
    1030        return sql

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

    1032    def with_sql(self, expression: exp.With) -> str:
    1033        sql = self.expressions(expression, flat=True)
    1034        recursive = (
    1035            "RECURSIVE "
    1036            if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive")
    1037            else ""
    1038        )
    1039
    1040        return f"WITH {recursive}{sql}"

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

    1042    def cte_sql(self, expression: exp.CTE) -> str:
    1043        alias = self.sql(expression, "alias")
    1044        return f"{alias} AS {self.wrap(expression)}"

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

    1046    def tablealias_sql(self, expression: exp.TableAlias) -> str:
    1047        alias = self.sql(expression, "this")
    1048        columns = self.expressions(expression, key="columns", flat=True)
    1049        columns = f"({columns})" if columns else ""
    1050
    1051        if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS:
    1052            columns = ""
    1053            self.unsupported("Named columns are not supported in table alias.")
    1054
    1055        if not alias and not self.dialect.UNNEST_COLUMN_ONLY:
    1056            alias = "_t"
    1057
    1058        return f"{alias}{columns}"

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

    1060    def bitstring_sql(self, expression: exp.BitString) -> str:
    1061        this = self.sql(expression, "this")
    1062        if self.dialect.BIT_START:
    1063            return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}"
    1064        return f"{int(this, 2)}"

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

    1066    def hexstring_sql(self, expression: exp.HexString) -> str:
    1067        this = self.sql(expression, "this")
    1068        if self.dialect.HEX_START:
    1069            return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}"
    1070        return f"{int(this, 16)}"

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

    1072    def bytestring_sql(self, expression: exp.ByteString) -> str:
    1073        this = self.sql(expression, "this")
    1074        if self.dialect.BYTE_START:
    1075            return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}"
    1076        return this

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

    1078    def unicodestring_sql(self, expression: exp.UnicodeString) -> str:
    1079        this = self.sql(expression, "this")
    1080        escape = expression.args.get("escape")
    1081
    1082        if self.dialect.UNICODE_START:
    1083            escape = f" UESCAPE {self.sql(escape)}" if escape else ""
    1084            return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}"
    1085
    1086        if escape:
    1087            pattern = re.compile(rf"{escape.name}(\d+)")
    1088        else:
    1089            pattern = ESCAPED_UNICODE_RE
    1090
    1091        this = pattern.sub(r"\\u\1", this)
    1092        return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"

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

    1094    def rawstring_sql(self, expression: exp.RawString) -> str:
    1095        string = self.escape_str(expression.this.replace("\\", "\\\\"))
    1096        return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}"

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

    1098    def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str:
    1099        this = self.sql(expression, "this")
    1100        specifier = self.sql(expression, "expression")
    1101        specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else ""
    1102        return f"{this}{specifier}"

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

    1104    def datatype_sql(self, expression: exp.DataType) -> str:
    1105        type_value = expression.this
    1106
    1107        if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"):
    1108            type_sql = self.sql(expression, "kind")
    1109        else:
    1110            type_sql = (
    1111                self.TYPE_MAPPING.get(type_value, type_value.value)
    1112                if isinstance(type_value, exp.DataType.Type)
    1113                else type_value
    1114            )
    1115
    1116        nested = ""
    1117        interior = self.expressions(expression, flat=True)
    1118        values = ""
    1119
    1120        if interior:
    1121            if expression.args.get("nested"):
    1122                nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}"
    1123                if expression.args.get("values") is not None:
    1124                    delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")")
    1125                    values = self.expressions(expression, key="values", flat=True)
    1126                    values = f"{delimiters[0]}{values}{delimiters[1]}"
    1127            elif type_value == exp.DataType.Type.INTERVAL:
    1128                nested = f" {interior}"
    1129            else:
    1130                nested = f"({interior})"
    1131
    1132        type_sql = f"{type_sql}{nested}{values}"
    1133        if self.TZ_TO_WITH_TIME_ZONE and type_value in (
    1134            exp.DataType.Type.TIMETZ,
    1135            exp.DataType.Type.TIMESTAMPTZ,
    1136        ):
    1137            type_sql = f"{type_sql} WITH TIME ZONE"
    1138
    1139        return type_sql

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

    1141    def directory_sql(self, expression: exp.Directory) -> str:
    1142        local = "LOCAL " if expression.args.get("local") else ""
    1143        row_format = self.sql(expression, "row_format")
    1144        row_format = f" {row_format}" if row_format else ""
    1145        return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"

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

    1147    def delete_sql(self, expression: exp.Delete) -> str:
    1148        this = self.sql(expression, "this")
    1149        this = f" FROM {this}" if this else ""
    1150        using = self.sql(expression, "using")
    1151        using = f" USING {using}" if using else ""
    1152        where = self.sql(expression, "where")
    1153        returning = self.sql(expression, "returning")
    1154        limit = self.sql(expression, "limit")
    1155        tables = self.expressions(expression, key="tables")
    1156        tables = f" {tables}" if tables else ""
    1157        if self.RETURNING_END:
    1158            expression_sql = f"{this}{using}{where}{returning}{limit}"
    1159        else:
    1160            expression_sql = f"{returning}{this}{using}{where}{limit}"
    1161        return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")

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

    1163    def drop_sql(self, expression: exp.Drop) -> str:
    1164        this = self.sql(expression, "this")
    1165        kind = expression.args["kind"]
    1166        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    1167        temporary = " TEMPORARY" if expression.args.get("temporary") else ""
    1168        materialized = " MATERIALIZED" if expression.args.get("materialized") else ""
    1169        cascade = " CASCADE" if expression.args.get("cascade") else ""
    1170        constraints = " CONSTRAINTS" if expression.args.get("constraints") else ""
    1171        purge = " PURGE" if expression.args.get("purge") else ""
    1172        return (
    1173            f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}"
    1174        )

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

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

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

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

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

    1182    def fetch_sql(self, expression: exp.Fetch) -> str:
    1183        direction = expression.args.get("direction")
    1184        direction = f" {direction}" if direction else ""
    1185        count = expression.args.get("count")
    1186        count = f" {count}" if count else ""
    1187        if expression.args.get("percent"):
    1188            count = f"{count} PERCENT"
    1189        with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY"
    1190        return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"

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

    1192    def filter_sql(self, expression: exp.Filter) -> str:
    1193        if self.AGGREGATE_FILTER_SUPPORTED:
    1194            this = self.sql(expression, "this")
    1195            where = self.sql(expression, "expression").strip()
    1196            return f"{this} FILTER({where})"
    1197
    1198        agg = expression.this
    1199        agg_arg = agg.this
    1200        cond = expression.expression.this
    1201        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy()))
    1202        return self.sql(agg)

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

    1204    def hint_sql(self, expression: exp.Hint) -> str:
    1205        if not self.QUERY_HINTS:
    1206            self.unsupported("Hints are not supported")
    1207            return ""
    1208
    1209        return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */"

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

    1211    def index_sql(self, expression: exp.Index) -> str:
    1212        unique = "UNIQUE " if expression.args.get("unique") else ""
    1213        primary = "PRIMARY " if expression.args.get("primary") else ""
    1214        amp = "AMP " if expression.args.get("amp") else ""
    1215        name = self.sql(expression, "this")
    1216        name = f"{name} " if name else ""
    1217        table = self.sql(expression, "table")
    1218        table = f"{self.INDEX_ON} {table}" if table else ""
    1219        using = self.sql(expression, "using")
    1220        using = f" USING {using}" if using else ""
    1221        index = "INDEX " if not table else ""
    1222        columns = self.expressions(expression, key="columns", flat=True)
    1223        columns = f"({columns})" if columns else ""
    1224        partition_by = self.expressions(expression, key="partition_by", flat=True)
    1225        partition_by = f" PARTITION BY {partition_by}" if partition_by else ""
    1226        where = self.sql(expression, "where")
    1227        include = self.expressions(expression, key="include", flat=True)
    1228        if include:
    1229            include = f" INCLUDE ({include})"
    1230        return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{include}{partition_by}{where}"

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

    1232    def identifier_sql(self, expression: exp.Identifier) -> str:
    1233        text = expression.name
    1234        lower = text.lower()
    1235        text = lower if self.normalize and not expression.quoted else text
    1236        text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end)
    1237        if (
    1238            expression.quoted
    1239            or self.dialect.can_identify(text, self.identify)
    1240            or lower in self.RESERVED_KEYWORDS
    1241            or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit())
    1242        ):
    1243            text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}"
    1244        return text

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

    1246    def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str:
    1247        input_format = self.sql(expression, "input_format")
    1248        input_format = f"INPUTFORMAT {input_format}" if input_format else ""
    1249        output_format = self.sql(expression, "output_format")
    1250        output_format = f"OUTPUTFORMAT {output_format}" if output_format else ""
    1251        return self.sep().join((input_format, output_format))

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

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

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

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

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

    1260    def properties_sql(self, expression: exp.Properties) -> str:
    1261        root_properties = []
    1262        with_properties = []
    1263
    1264        for p in expression.expressions:
    1265            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1266            if p_loc == exp.Properties.Location.POST_WITH:
    1267                with_properties.append(p)
    1268            elif p_loc == exp.Properties.Location.POST_SCHEMA:
    1269                root_properties.append(p)
    1270
    1271        return self.root_properties(
    1272            exp.Properties(expressions=root_properties)
    1273        ) + self.with_properties(exp.Properties(expressions=with_properties))

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

    1275    def root_properties(self, properties: exp.Properties) -> str:
    1276        if properties.expressions:
    1277            return self.sep() + self.expressions(properties, indent=False, sep=" ")
    1278        return ""

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

    1280    def properties(
    1281        self,
    1282        properties: exp.Properties,
    1283        prefix: str = "",
    1284        sep: str = ", ",
    1285        suffix: str = "",
    1286        wrapped: bool = True,
    1287    ) -> str:
    1288        if properties.expressions:
    1289            expressions = self.expressions(properties, sep=sep, indent=False)
    1290            if expressions:
    1291                expressions = self.wrap(expressions) if wrapped else expressions
    1292                return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}"
    1293        return ""

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

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

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

    1298    def locate_properties(self, properties: exp.Properties) -> t.DefaultDict:
    1299        properties_locs = defaultdict(list)
    1300        for p in properties.expressions:
    1301            p_loc = self.PROPERTIES_LOCATION[p.__class__]
    1302            if p_loc != exp.Properties.Location.UNSUPPORTED:
    1303                properties_locs[p_loc].append(p)
    1304            else:
    1305                self.unsupported(f"Unsupported property {p.key}")
    1306
    1307        return properties_locs

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

    1309    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
    1310        if isinstance(expression.this, exp.Dot):
    1311            return self.sql(expression, "this")
    1312        return f"'{expression.name}'" if string_key else expression.name

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

    1314    def property_sql(self, expression: exp.Property) -> str:
    1315        property_cls = expression.__class__
    1316        if property_cls == exp.Property:
    1317            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
    1318
    1319        property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
    1320        if not property_name:
    1321            self.unsupported(f"Unsupported property {expression.key}")
    1322
    1323        return f"{property_name}={self.sql(expression, 'this')}"

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

    1325    def likeproperty_sql(self, expression: exp.LikeProperty) -> str:
    1326        if self.SUPPORTS_CREATE_TABLE_LIKE:
    1327            options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions)
    1328            options = f" {options}" if options else ""
    1329
    1330            like = f"LIKE {self.sql(expression, 'this')}{options}"
    1331            if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema):
    1332                like = f"({like})"
    1333
    1334            return like
    1335
    1336        if expression.expressions:
    1337            self.unsupported("Transpilation of LIKE property options is unsupported")
    1338
    1339        select = exp.select("*").from_(expression.this).limit(0)
    1340        return f"AS {self.sql(select)}"

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

    1342    def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str:
    1343        no = "NO " if expression.args.get("no") else ""
    1344        protection = " PROTECTION" if expression.args.get("protection") else ""
    1345        return f"{no}FALLBACK{protection}"

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

    1347    def journalproperty_sql(self, expression: exp.JournalProperty) -> str:
    1348        no = "NO " if expression.args.get("no") else ""
    1349        local = expression.args.get("local")
    1350        local = f"{local} " if local else ""
    1351        dual = "DUAL " if expression.args.get("dual") else ""
    1352        before = "BEFORE " if expression.args.get("before") else ""
    1353        after = "AFTER " if expression.args.get("after") else ""
    1354        return f"{no}{local}{dual}{before}{after}JOURNAL"

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

    1356    def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str:
    1357        freespace = self.sql(expression, "this")
    1358        percent = " PERCENT" if expression.args.get("percent") else ""
    1359        return f"FREESPACE={freespace}{percent}"

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

    1361    def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str:
    1362        if expression.args.get("default"):
    1363            property = "DEFAULT"
    1364        elif expression.args.get("on"):
    1365            property = "ON"
    1366        else:
    1367            property = "OFF"
    1368        return f"CHECKSUM={property}"

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

    1370    def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str:
    1371        if expression.args.get("no"):
    1372            return "NO MERGEBLOCKRATIO"
    1373        if expression.args.get("default"):
    1374            return "DEFAULT MERGEBLOCKRATIO"
    1375
    1376        percent = " PERCENT" if expression.args.get("percent") else ""
    1377        return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"

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

    1379    def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str:
    1380        default = expression.args.get("default")
    1381        minimum = expression.args.get("minimum")
    1382        maximum = expression.args.get("maximum")
    1383        if default or minimum or maximum:
    1384            if default:
    1385                prop = "DEFAULT"
    1386            elif minimum:
    1387                prop = "MINIMUM"
    1388            else:
    1389                prop = "MAXIMUM"
    1390            return f"{prop} DATABLOCKSIZE"
    1391        units = expression.args.get("units")
    1392        units = f" {units}" if units else ""
    1393        return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"

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

    1395    def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str:
    1396        autotemp = expression.args.get("autotemp")
    1397        always = expression.args.get("always")
    1398        default = expression.args.get("default")
    1399        manual = expression.args.get("manual")
    1400        never = expression.args.get("never")
    1401
    1402        if autotemp is not None:
    1403            prop = f"AUTOTEMP({self.expressions(autotemp)})"
    1404        elif always:
    1405            prop = "ALWAYS"
    1406        elif default:
    1407            prop = "DEFAULT"
    1408        elif manual:
    1409            prop = "MANUAL"
    1410        elif never:
    1411            prop = "NEVER"
    1412        return f"BLOCKCOMPRESSION={prop}"

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

    1414    def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str:
    1415        no = expression.args.get("no")
    1416        no = " NO" if no else ""
    1417        concurrent = expression.args.get("concurrent")
    1418        concurrent = " CONCURRENT" if concurrent else ""
    1419
    1420        for_ = ""
    1421        if expression.args.get("for_all"):
    1422            for_ = " FOR ALL"
    1423        elif expression.args.get("for_insert"):
    1424            for_ = " FOR INSERT"
    1425        elif expression.args.get("for_none"):
    1426            for_ = " FOR NONE"
    1427        return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"

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

    1429    def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str:
    1430        if isinstance(expression.this, list):
    1431            return f"IN ({self.expressions(expression, key='this', flat=True)})"
    1432        if expression.this:
    1433            modulus = self.sql(expression, "this")
    1434            remainder = self.sql(expression, "expression")
    1435            return f"WITH (MODULUS {modulus}, REMAINDER {remainder})"
    1436
    1437        from_expressions = self.expressions(expression, key="from_expressions", flat=True)
    1438        to_expressions = self.expressions(expression, key="to_expressions", flat=True)
    1439        return f"FROM ({from_expressions}) TO ({to_expressions})"

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

    1441    def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str:
    1442        this = self.sql(expression, "this")
    1443
    1444        for_values_or_default = expression.expression
    1445        if isinstance(for_values_or_default, exp.PartitionBoundSpec):
    1446            for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}"
    1447        else:
    1448            for_values_or_default = " DEFAULT"
    1449
    1450        return f"PARTITION OF {this}{for_values_or_default}"

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

    1452    def lockingproperty_sql(self, expression: exp.LockingProperty) -> str:
    1453        kind = expression.args.get("kind")
    1454        this = f" {self.sql(expression, 'this')}" if expression.this else ""
    1455        for_or_in = expression.args.get("for_or_in")
    1456        for_or_in = f" {for_or_in}" if for_or_in else ""
    1457        lock_type = expression.args.get("lock_type")
    1458        override = " OVERRIDE" if expression.args.get("override") else ""
    1459        return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"

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

    1461    def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str:
    1462        data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
    1463        statistics = expression.args.get("statistics")
    1464        statistics_sql = ""
    1465        if statistics is not None:
    1466            statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS"
    1467        return f"{data_sql}{statistics_sql}"

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

    1469    def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str:
    1470        sql = "WITH(SYSTEM_VERSIONING=ON"
    1471
    1472        if expression.this:
    1473            history_table = self.sql(expression, "this")
    1474            sql = f"{sql}(HISTORY_TABLE={history_table}"
    1475
    1476            if expression.expression:
    1477                data_consistency_check = self.sql(expression, "expression")
    1478                sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}"
    1479
    1480            sql = f"{sql})"
    1481
    1482        return f"{sql})"

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

    1484    def insert_sql(self, expression: exp.Insert) -> str:
    1485        hint = self.sql(expression, "hint")
    1486        overwrite = expression.args.get("overwrite")
    1487
    1488        if isinstance(expression.this, exp.Directory):
    1489            this = " OVERWRITE" if overwrite else " INTO"
    1490        else:
    1491            this = self.INSERT_OVERWRITE if overwrite else " INTO"
    1492
    1493        alternative = expression.args.get("alternative")
    1494        alternative = f" OR {alternative}" if alternative else ""
    1495        ignore = " IGNORE" if expression.args.get("ignore") else ""
    1496
    1497        this = f"{this} {self.sql(expression, 'this')}"
    1498
    1499        exists = " IF EXISTS" if expression.args.get("exists") else ""
    1500        partition_sql = (
    1501            f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else ""
    1502        )
    1503        where = self.sql(expression, "where")
    1504        where = f"{self.sep()}REPLACE WHERE {where}" if where else ""
    1505        expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}"
    1506        on_conflict = self.sql(expression, "conflict")
    1507        on_conflict = f" {on_conflict}" if on_conflict else ""
    1508        by_name = " BY NAME" if expression.args.get("by_name") else ""
    1509        returning = self.sql(expression, "returning")
    1510
    1511        if self.RETURNING_END:
    1512            expression_sql = f"{expression_sql}{on_conflict}{returning}"
    1513        else:
    1514            expression_sql = f"{returning}{expression_sql}{on_conflict}"
    1515
    1516        sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}"
    1517        return self.prepend_ctes(expression, sql)

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

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

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

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

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

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

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

    1528    def kill_sql(self, expression: exp.Kill) -> str:
    1529        kind = self.sql(expression, "kind")
    1530        kind = f" {kind}" if kind else ""
    1531        this = self.sql(expression, "this")
    1532        this = f" {this}" if this else ""
    1533        return f"KILL{kind}{this}"

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

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

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

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

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

    1541    def onconflict_sql(self, expression: exp.OnConflict) -> str:
    1542        conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT"
    1543
    1544        constraint = self.sql(expression, "constraint")
    1545        constraint = f" ON CONSTRAINT {constraint}" if constraint else ""
    1546
    1547        conflict_keys = self.expressions(expression, key="conflict_keys", flat=True)
    1548        conflict_keys = f"({conflict_keys}) " if conflict_keys else " "
    1549        action = self.sql(expression, "action")
    1550
    1551        expressions = self.expressions(expression, flat=True)
    1552        if expressions:
    1553            set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else ""
    1554            expressions = f" {set_keyword}{expressions}"
    1555
    1556        return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"

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

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

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

    1561    def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str:
    1562        fields = expression.args.get("fields")
    1563        fields = f" FIELDS TERMINATED BY {fields}" if fields else ""
    1564        escaped = expression.args.get("escaped")
    1565        escaped = f" ESCAPED BY {escaped}" if escaped else ""
    1566        items = expression.args.get("collection_items")
    1567        items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else ""
    1568        keys = expression.args.get("map_keys")
    1569        keys = f" MAP KEYS TERMINATED BY {keys}" if keys else ""
    1570        lines = expression.args.get("lines")
    1571        lines = f" LINES TERMINATED BY {lines}" if lines else ""
    1572        null = expression.args.get("null")
    1573        null = f" NULL DEFINED AS {null}" if null else ""
    1574        return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"

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

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

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

    1579    def indextablehint_sql(self, expression: exp.IndexTableHint) -> str:
    1580        this = f"{self.sql(expression, 'this')} INDEX"
    1581        target = self.sql(expression, "target")
    1582        target = f" FOR {target}" if target else ""
    1583        return f"{this}{target} ({self.expressions(expression, flat=True)})"

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

    1585    def historicaldata_sql(self, expression: exp.HistoricalData) -> str:
    1586        this = self.sql(expression, "this")
    1587        kind = self.sql(expression, "kind")
    1588        expr = self.sql(expression, "expression")
    1589        return f"{this} ({kind} => {expr})"

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

    1591    def table_parts(self, expression: exp.Table) -> str:
    1592        return ".".join(
    1593            self.sql(part)
    1594            for part in (
    1595                expression.args.get("catalog"),
    1596                expression.args.get("db"),
    1597                expression.args.get("this"),
    1598            )
    1599            if part is not None
    1600        )

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

    1602    def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str:
    1603        table = self.table_parts(expression)
    1604        only = "ONLY " if expression.args.get("only") else ""
    1605        version = self.sql(expression, "version")
    1606        version = f" {version}" if version else ""
    1607        alias = self.sql(expression, "alias")
    1608        alias = f"{sep}{alias}" if alias else ""
    1609        hints = self.expressions(expression, key="hints", sep=" ")
    1610        hints = f" {hints}" if hints and self.TABLE_HINTS else ""
    1611        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    1612        pivots = f" {pivots}" if pivots else ""
    1613        joins = self.expressions(expression, key="joins", sep="", skip_first=True)
    1614        laterals = self.expressions(expression, key="laterals", sep="")
    1615
    1616        file_format = self.sql(expression, "format")
    1617        if file_format:
    1618            pattern = self.sql(expression, "pattern")
    1619            pattern = f", PATTERN => {pattern}" if pattern else ""
    1620            file_format = f" (FILE_FORMAT => {file_format}{pattern})"
    1621
    1622        ordinality = expression.args.get("ordinality") or ""
    1623        if ordinality:
    1624            ordinality = f" WITH ORDINALITY{alias}"
    1625            alias = ""
    1626
    1627        when = self.sql(expression, "when")
    1628        if when:
    1629            table = f"{table} {when}"
    1630
    1631        return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"

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

    1633    def tablesample_sql(
    1634        self,
    1635        expression: exp.TableSample,
    1636        sep: str = " AS ",
    1637        tablesample_keyword: t.Optional[str] = None,
    1638    ) -> str:
    1639        if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias:
    1640            table = expression.this.copy()
    1641            table.set("alias", None)
    1642            this = self.sql(table)
    1643            alias = f"{sep}{self.sql(expression.this, 'alias')}"
    1644        else:
    1645            this = self.sql(expression, "this")
    1646            alias = ""
    1647
    1648        method = self.sql(expression, "method")
    1649        method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else ""
    1650        numerator = self.sql(expression, "bucket_numerator")
    1651        denominator = self.sql(expression, "bucket_denominator")
    1652        field = self.sql(expression, "bucket_field")
    1653        field = f" ON {field}" if field else ""
    1654        bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else ""
    1655        seed = self.sql(expression, "seed")
    1656        seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else ""
    1657
    1658        size = self.sql(expression, "size")
    1659        if size and self.TABLESAMPLE_SIZE_IS_ROWS:
    1660            size = f"{size} ROWS"
    1661
    1662        percent = self.sql(expression, "percent")
    1663        if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
    1664            percent = f"{percent} PERCENT"
    1665
    1666        expr = f"{bucket}{percent}{size}"
    1667        if self.TABLESAMPLE_REQUIRES_PARENS:
    1668            expr = f"({expr})"
    1669
    1670        return (
    1671            f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}"
    1672        )

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

    1674    def pivot_sql(self, expression: exp.Pivot) -> str:
    1675        expressions = self.expressions(expression, flat=True)
    1676
    1677        if expression.this:
    1678            this = self.sql(expression, "this")
    1679            if not expressions:
    1680                return f"UNPIVOT {this}"
    1681
    1682            on = f"{self.seg('ON')} {expressions}"
    1683            using = self.expressions(expression, key="using", flat=True)
    1684            using = f"{self.seg('USING')} {using}" if using else ""
    1685            group = self.sql(expression, "group")
    1686            return f"PIVOT {this}{on}{using}{group}"
    1687
    1688        alias = self.sql(expression, "alias")
    1689        alias = f" AS {alias}" if alias else ""
    1690        direction = "UNPIVOT" if expression.unpivot else "PIVOT"
    1691        field = self.sql(expression, "field")
    1692        include_nulls = expression.args.get("include_nulls")
    1693        if include_nulls is not None:
    1694            nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS "
    1695        else:
    1696            nulls = ""
    1697        return f"{direction}{nulls}({expressions} FOR {field}){alias}"

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

    1699    def version_sql(self, expression: exp.Version) -> str:
    1700        this = f"FOR {expression.name}"
    1701        kind = expression.text("kind")
    1702        expr = self.sql(expression, "expression")
    1703        return f"{this} {kind} {expr}"

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

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

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

    1708    def update_sql(self, expression: exp.Update) -> str:
    1709        this = self.sql(expression, "this")
    1710        set_sql = self.expressions(expression, flat=True)
    1711        from_sql = self.sql(expression, "from")
    1712        where_sql = self.sql(expression, "where")
    1713        returning = self.sql(expression, "returning")
    1714        order = self.sql(expression, "order")
    1715        limit = self.sql(expression, "limit")
    1716        if self.RETURNING_END:
    1717            expression_sql = f"{from_sql}{where_sql}{returning}"
    1718        else:
    1719            expression_sql = f"{returning}{from_sql}{where_sql}"
    1720        sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}"
    1721        return self.prepend_ctes(expression, sql)

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

    1723    def values_sql(self, expression: exp.Values) -> str:
    1724        # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
    1725        if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
    1726            args = self.expressions(expression)
    1727            alias = self.sql(expression, "alias")
    1728            values = f"VALUES{self.seg('')}{args}"
    1729            values = (
    1730                f"({values})"
    1731                if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
    1732                else values
    1733            )
    1734            return f"{values} AS {alias}" if alias else values
    1735
    1736        # Converts `VALUES...` expression into a series of select unions.
    1737        alias_node = expression.args.get("alias")
    1738        column_names = alias_node and alias_node.columns
    1739
    1740        selects: t.List[exp.Query] = []
    1741
    1742        for i, tup in enumerate(expression.expressions):
    1743            row = tup.expressions
    1744
    1745            if i == 0 and column_names:
    1746                row = [
    1747                    exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
    1748                ]
    1749
    1750            selects.append(exp.Select(expressions=row))
    1751
    1752        if self.pretty:
    1753            # This may result in poor performance for large-cardinality `VALUES` tables, due to
    1754            # the deep nesting of the resulting exp.Unions. If this is a problem, either increase
    1755            # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`.
    1756            query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects)
    1757            return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False))
    1758
    1759        alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else ""
    1760        unions = " UNION ALL ".join(self.sql(select) for select in selects)
    1761        return f"({unions}){alias}"

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

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

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

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

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

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

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

    1774    def group_sql(self, expression: exp.Group) -> str:
    1775        group_by = self.op_expressions("GROUP BY", expression)
    1776
    1777        if expression.args.get("all"):
    1778            return f"{group_by} ALL"
    1779
    1780        grouping_sets = self.expressions(expression, key="grouping_sets", indent=False)
    1781        grouping_sets = (
    1782            f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else ""
    1783        )
    1784
    1785        cube = expression.args.get("cube", [])
    1786        if seq_get(cube, 0) is True:
    1787            return f"{group_by}{self.seg('WITH CUBE')}"
    1788        else:
    1789            cube_sql = self.expressions(expression, key="cube", indent=False)
    1790            cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else ""
    1791
    1792        rollup = expression.args.get("rollup", [])
    1793        if seq_get(rollup, 0) is True:
    1794            return f"{group_by}{self.seg('WITH ROLLUP')}"
    1795        else:
    1796            rollup_sql = self.expressions(expression, key="rollup", indent=False)
    1797            rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else ""
    1798
    1799        groupings = csv(
    1800            grouping_sets,
    1801            cube_sql,
    1802            rollup_sql,
    1803            self.seg("WITH TOTALS") if expression.args.get("totals") else "",
    1804            sep=self.GROUPINGS_SEP,
    1805        )
    1806
    1807        if expression.args.get("expressions") and groupings:
    1808            group_by = f"{group_by}{self.GROUPINGS_SEP}"
    1809
    1810        return f"{group_by}{groupings}"

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

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

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

    1816    def connect_sql(self, expression: exp.Connect) -> str:
    1817        start = self.sql(expression, "start")
    1818        start = self.seg(f"START WITH {start}") if start else ""
    1819        connect = self.sql(expression, "connect")
    1820        connect = self.seg(f"CONNECT BY {connect}")
    1821        return start + connect

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

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

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

    1826    def join_sql(self, expression: exp.Join) -> str:
    1827        if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"):
    1828            side = None
    1829        else:
    1830            side = expression.side
    1831
    1832        op_sql = " ".join(
    1833            op
    1834            for op in (
    1835                expression.method,
    1836                "GLOBAL" if expression.args.get("global") else None,
    1837                side,
    1838                expression.kind,
    1839                expression.hint if self.JOIN_HINTS else None,
    1840            )
    1841            if op
    1842        )
    1843        on_sql = self.sql(expression, "on")
    1844        using = expression.args.get("using")
    1845
    1846        if not on_sql and using:
    1847            on_sql = csv(*(self.sql(column) for column in using))
    1848
    1849        this = expression.this
    1850        this_sql = self.sql(this)
    1851
    1852        if on_sql:
    1853            on_sql = self.indent(on_sql, skip_first=True)
    1854            space = self.seg(" " * self.pad) if self.pretty else " "
    1855            if using:
    1856                on_sql = f"{space}USING ({on_sql})"
    1857            else:
    1858                on_sql = f"{space}ON {on_sql}"
    1859        elif not op_sql:
    1860            if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None:
    1861                return f" {this_sql}"
    1862
    1863            return f", {this_sql}"
    1864
    1865        op_sql = f"{op_sql} JOIN" if op_sql else "JOIN"
    1866        return f"{self.seg(op_sql)} {this_sql}{on_sql}"

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

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

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

    1873    def lateral_op(self, expression: exp.Lateral) -> str:
    1874        cross_apply = expression.args.get("cross_apply")
    1875
    1876        # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/
    1877        if cross_apply is True:
    1878            op = "INNER JOIN "
    1879        elif cross_apply is False:
    1880            op = "LEFT JOIN "
    1881        else:
    1882            op = ""
    1883
    1884        return f"{op}LATERAL"

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

    1886    def lateral_sql(self, expression: exp.Lateral) -> str:
    1887        this = self.sql(expression, "this")
    1888
    1889        if expression.args.get("view"):
    1890            alias = expression.args["alias"]
    1891            columns = self.expressions(alias, key="columns", flat=True)
    1892            table = f" {alias.name}" if alias.name else ""
    1893            columns = f" AS {columns}" if columns else ""
    1894            op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}")
    1895            return f"{op_sql}{self.sep()}{this}{table}{columns}"
    1896
    1897        alias = self.sql(expression, "alias")
    1898        alias = f" AS {alias}" if alias else ""
    1899        return f"{self.lateral_op(expression)} {this}{alias}"

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

    1901    def limit_sql(self, expression: exp.Limit, top: bool = False) -> str:
    1902        this = self.sql(expression, "this")
    1903
    1904        args = [
    1905            self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e
    1906            for e in (expression.args.get(k) for k in ("offset", "expression"))
    1907            if e
    1908        ]
    1909
    1910        args_sql = ", ".join(self.sql(e) for e in args)
    1911        args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql
    1912        expressions = self.expressions(expression, flat=True)
    1913        expressions = f" BY {expressions}" if expressions else ""
    1914
    1915        return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"

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

    1917    def offset_sql(self, expression: exp.Offset) -> str:
    1918        this = self.sql(expression, "this")
    1919        value = expression.expression
    1920        value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value
    1921        expressions = self.expressions(expression, flat=True)
    1922        expressions = f" BY {expressions}" if expressions else ""
    1923        return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"

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

    1925    def setitem_sql(self, expression: exp.SetItem) -> str:
    1926        kind = self.sql(expression, "kind")
    1927        kind = f"{kind} " if kind else ""
    1928        this = self.sql(expression, "this")
    1929        expressions = self.expressions(expression)
    1930        collate = self.sql(expression, "collate")
    1931        collate = f" COLLATE {collate}" if collate else ""
    1932        global_ = "GLOBAL " if expression.args.get("global") else ""
    1933        return f"{global_}{kind}{this}{expressions}{collate}"

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

    1935    def set_sql(self, expression: exp.Set) -> str:
    1936        expressions = (
    1937            f" {self.expressions(expression, flat=True)}" if expression.expressions else ""
    1938        )
    1939        tag = " TAG" if expression.args.get("tag") else ""
    1940        return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"

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

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

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

    1945    def lock_sql(self, expression: exp.Lock) -> str:
    1946        if not self.LOCKING_READS_SUPPORTED:
    1947            self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported")
    1948            return ""
    1949
    1950        lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE"
    1951        expressions = self.expressions(expression, flat=True)
    1952        expressions = f" OF {expressions}" if expressions else ""
    1953        wait = expression.args.get("wait")
    1954
    1955        if wait is not None:
    1956            if isinstance(wait, exp.Literal):
    1957                wait = f" WAIT {self.sql(wait)}"
    1958            else:
    1959                wait = " NOWAIT" if wait else " SKIP LOCKED"
    1960
    1961        return f"{lock_type}{expressions}{wait or ''}"

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

    1963    def literal_sql(self, expression: exp.Literal) -> str:
    1964        text = expression.this or ""
    1965        if expression.is_string:
    1966            text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}"
    1967        return text

def escape_str(self, text: str) -> str: View Source

    1969    def escape_str(self, text: str) -> str:
    1970        text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
    1971        if self.dialect.INVERSE_ESCAPE_SEQUENCES:
    1972            text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text)
    1973        elif self.pretty:
    1974            text = text.replace("\n", self.SENTINEL_LINE_BREAK)
    1975        return text

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

    1977    def loaddata_sql(self, expression: exp.LoadData) -> str:
    1978        local = " LOCAL" if expression.args.get("local") else ""
    1979        inpath = f" INPATH {self.sql(expression, 'inpath')}"
    1980        overwrite = " OVERWRITE" if expression.args.get("overwrite") else ""
    1981        this = f" INTO TABLE {self.sql(expression, 'this')}"
    1982        partition = self.sql(expression, "partition")
    1983        partition = f" {partition}" if partition else ""
    1984        input_format = self.sql(expression, "input_format")
    1985        input_format = f" INPUTFORMAT {input_format}" if input_format else ""
    1986        serde = self.sql(expression, "serde")
    1987        serde = f" SERDE {serde}" if serde else ""
    1988        return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"

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

    1990    def null_sql(self, *_) -> str:
    1991        return "NULL"

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

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

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

    1996    def order_sql(self, expression: exp.Order, flat: bool = False) -> str:
    1997        this = self.sql(expression, "this")
    1998        this = f"{this} " if this else this
    1999        siblings = "SIBLINGS " if expression.args.get("siblings") else ""
    2000        order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat)  # type: ignore
    2001        interpolated_values = [
    2002            f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}"
    2003            for named_expression in expression.args.get("interpolate") or []
    2004        ]
    2005        interpolate = (
    2006            f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else ""
    2007        )
    2008        return f"{order}{interpolate}"

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

    2010    def withfill_sql(self, expression: exp.WithFill) -> str:
    2011        from_sql = self.sql(expression, "from")
    2012        from_sql = f" FROM {from_sql}" if from_sql else ""
    2013        to_sql = self.sql(expression, "to")
    2014        to_sql = f" TO {to_sql}" if to_sql else ""
    2015        step_sql = self.sql(expression, "step")
    2016        step_sql = f" STEP {step_sql}" if step_sql else ""
    2017        return f"WITH FILL{from_sql}{to_sql}{step_sql}"

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

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

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

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

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

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

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

    2028    def ordered_sql(self, expression: exp.Ordered) -> str:
    2029        desc = expression.args.get("desc")
    2030        asc = not desc
    2031
    2032        nulls_first = expression.args.get("nulls_first")
    2033        nulls_last = not nulls_first
    2034        nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large"
    2035        nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small"
    2036        nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last"
    2037
    2038        this = self.sql(expression, "this")
    2039
    2040        sort_order = " DESC" if desc else (" ASC" if desc is False else "")
    2041        nulls_sort_change = ""
    2042        if nulls_first and (
    2043            (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last
    2044        ):
    2045            nulls_sort_change = " NULLS FIRST"
    2046        elif (
    2047            nulls_last
    2048            and ((asc and nulls_are_small) or (desc and nulls_are_large))
    2049            and not nulls_are_last
    2050        ):
    2051            nulls_sort_change = " NULLS LAST"
    2052
    2053        # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it
    2054        if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED:
    2055            window = expression.find_ancestor(exp.Window, exp.Select)
    2056            if isinstance(window, exp.Window) and window.args.get("spec"):
    2057                self.unsupported(
    2058                    f"'{nulls_sort_change.strip()}' translation not supported in window functions"
    2059                )
    2060                nulls_sort_change = ""
    2061            elif self.NULL_ORDERING_SUPPORTED is None:
    2062                if expression.this.is_int:
    2063                    self.unsupported(
    2064                        f"'{nulls_sort_change.strip()}' translation not supported with positional ordering"
    2065                    )
    2066                else:
    2067                    null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else ""
    2068                    this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}"
    2069                nulls_sort_change = ""
    2070
    2071        with_fill = self.sql(expression, "with_fill")
    2072        with_fill = f" {with_fill}" if with_fill else ""
    2073
    2074        return f"{this}{sort_order}{nulls_sort_change}{with_fill}"

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

    2076    def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str:
    2077        partition = self.partition_by_sql(expression)
    2078        order = self.sql(expression, "order")
    2079        measures = self.expressions(expression, key="measures")
    2080        measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else ""
    2081        rows = self.sql(expression, "rows")
    2082        rows = self.seg(rows) if rows else ""
    2083        after = self.sql(expression, "after")
    2084        after = self.seg(after) if after else ""
    2085        pattern = self.sql(expression, "pattern")
    2086        pattern = self.seg(f"PATTERN ({pattern})") if pattern else ""
    2087        definition_sqls = [
    2088            f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}"
    2089            for definition in expression.args.get("define", [])
    2090        ]
    2091        definitions = self.expressions(sqls=definition_sqls)
    2092        define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else ""
    2093        body = "".join(
    2094            (
    2095                partition,
    2096                order,
    2097                measures,
    2098                rows,
    2099                after,
    2100                pattern,
    2101                define,
    2102            )
    2103        )
    2104        alias = self.sql(expression, "alias")
    2105        alias = f" {alias}" if alias else ""
    2106        return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"

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

    2108    def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str:
    2109        limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit")
    2110
    2111        # If the limit is generated as TOP, we need to ensure it's not generated twice
    2112        with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP
    2113
    2114        if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch):
    2115            limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count")))
    2116        elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit):
    2117            limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression))
    2118
    2119        fetch = isinstance(limit, exp.Fetch)
    2120
    2121        offset_limit_modifiers = (
    2122            self.offset_limit_modifiers(expression, fetch, limit)
    2123            if with_offset_limit_modifiers
    2124            else []
    2125        )
    2126
    2127        options = self.expressions(expression, key="options")
    2128        if options:
    2129            options = f" OPTION{self.wrap(options)}"
    2130
    2131        return csv(
    2132            *sqls,
    2133            *[self.sql(join) for join in expression.args.get("joins") or []],
    2134            self.sql(expression, "connect"),
    2135            self.sql(expression, "match"),
    2136            *[self.sql(lateral) for lateral in expression.args.get("laterals") or []],
    2137            self.sql(expression, "prewhere"),
    2138            self.sql(expression, "where"),
    2139            self.sql(expression, "group"),
    2140            self.sql(expression, "having"),
    2141            *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()],
    2142            self.sql(expression, "order"),
    2143            *offset_limit_modifiers,
    2144            *self.after_limit_modifiers(expression),
    2145            options,
    2146            sep="",
    2147        )

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

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

    2152    def offset_limit_modifiers(
    2153        self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit]
    2154    ) -> t.List[str]:
    2155        return [
    2156            self.sql(expression, "offset") if fetch else self.sql(limit),
    2157            self.sql(limit) if fetch else self.sql(expression, "offset"),
    2158        ]

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

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

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

    2165    def select_sql(self, expression: exp.Select) -> str:
    2166        into = expression.args.get("into")
    2167        if not self.SUPPORTS_SELECT_INTO and into:
    2168            into.pop()
    2169
    2170        hint = self.sql(expression, "hint")
    2171        distinct = self.sql(expression, "distinct")
    2172        distinct = f" {distinct}" if distinct else ""
    2173        kind = self.sql(expression, "kind")
    2174        limit = expression.args.get("limit")
    2175        top = (
    2176            self.limit_sql(limit, top=True)
    2177            if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP
    2178            else ""
    2179        )
    2180
    2181        expressions = self.expressions(expression)
    2182
    2183        if kind:
    2184            if kind in self.SELECT_KINDS:
    2185                kind = f" AS {kind}"
    2186            else:
    2187                if kind == "STRUCT":
    2188                    expressions = self.expressions(
    2189                        sqls=[
    2190                            self.sql(
    2191                                exp.Struct(
    2192                                    expressions=[
    2193                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
    2194                                        if isinstance(e, exp.Alias)
    2195                                        else e
    2196                                        for e in expression.expressions
    2197                                    ]
    2198                                )
    2199                            )
    2200                        ]
    2201                    )
    2202                kind = ""
    2203
    2204        # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata
    2205        # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first.
    2206        top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}"
    2207        expressions = f"{self.sep()}{expressions}" if expressions else expressions
    2208        sql = self.query_modifiers(
    2209            expression,
    2210            f"SELECT{top_distinct}{kind}{expressions}",
    2211            self.sql(expression, "into", comment=False),
    2212            self.sql(expression, "from", comment=False),
    2213        )
    2214
    2215        sql = self.prepend_ctes(expression, sql)
    2216
    2217        if not self.SUPPORTS_SELECT_INTO and into:
    2218            if into.args.get("temporary"):
    2219                table_kind = " TEMPORARY"
    2220            elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"):
    2221                table_kind = " UNLOGGED"
    2222            else:
    2223                table_kind = ""
    2224            sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}"
    2225
    2226        return sql

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

    2228    def schema_sql(self, expression: exp.Schema) -> str:
    2229        this = self.sql(expression, "this")
    2230        sql = self.schema_columns_sql(expression)
    2231        return f"{this} {sql}" if this and sql else this or sql

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

    2233    def schema_columns_sql(self, expression: exp.Schema) -> str:
    2234        if expression.expressions:
    2235            return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}"
    2236        return ""

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

    2238    def star_sql(self, expression: exp.Star) -> str:
    2239        except_ = self.expressions(expression, key="except", flat=True)
    2240        except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else ""
    2241        replace = self.expressions(expression, key="replace", flat=True)
    2242        replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else ""
    2243        return f"*{except_}{replace}"

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

    2245    def parameter_sql(self, expression: exp.Parameter) -> str:
    2246        this = self.sql(expression, "this")
    2247        return f"{self.PARAMETER_TOKEN}{this}"

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

    2249    def sessionparameter_sql(self, expression: exp.SessionParameter) -> str:
    2250        this = self.sql(expression, "this")
    2251        kind = expression.text("kind")
    2252        if kind:
    2253            kind = f"{kind}."
    2254        return f"@@{kind}{this}"

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

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

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

    2259    def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str:
    2260        alias = self.sql(expression, "alias")
    2261        alias = f"{sep}{alias}" if alias else ""
    2262
    2263        pivots = self.expressions(expression, key="pivots", sep=" ", flat=True)
    2264        pivots = f" {pivots}" if pivots else ""
    2265
    2266        sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots)
    2267        return self.prepend_ctes(expression, sql)

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

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

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

    2273    def set_operations(self, expression: exp.Union) -> str:
    2274        sqls: t.List[str] = []
    2275        stack: t.List[t.Union[str, exp.Expression]] = [expression]
    2276
    2277        while stack:
    2278            node = stack.pop()
    2279
    2280            if isinstance(node, exp.Union):
    2281                stack.append(node.expression)
    2282                stack.append(
    2283                    self.maybe_comment(
    2284                        getattr(self, f"{node.key}_op")(node),
    2285                        expression=node.this,
    2286                        comments=node.comments,
    2287                    )
    2288                )
    2289                stack.append(node.this)
    2290            else:
    2291                sqls.append(self.sql(node))
    2292
    2293        this = self.sep().join(sqls)
    2294        this = self.query_modifiers(expression, this)
    2295        return self.prepend_ctes(expression, this)

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

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

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

    2300    def union_op(self, expression: exp.Union) -> str:
    2301        kind = " DISTINCT" if self.EXPLICIT_UNION else ""
    2302        kind = kind if expression.args.get("distinct") else " ALL"
    2303        by_name = " BY NAME" if expression.args.get("by_name") else ""
    2304        return f"UNION{kind}{by_name}"

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

    2306    def unnest_sql(self, expression: exp.Unnest) -> str:
    2307        args = self.expressions(expression, flat=True)
    2308
    2309        alias = expression.args.get("alias")
    2310        offset = expression.args.get("offset")
    2311
    2312        if self.UNNEST_WITH_ORDINALITY:
    2313            if alias and isinstance(offset, exp.Expression):
    2314                alias.append("columns", offset)
    2315
    2316        if alias and self.dialect.UNNEST_COLUMN_ONLY:
    2317            columns = alias.columns
    2318            alias = self.sql(columns[0]) if columns else ""
    2319        else:
    2320            alias = self.sql(alias)
    2321
    2322        alias = f" AS {alias}" if alias else alias
    2323        if self.UNNEST_WITH_ORDINALITY:
    2324            suffix = f" WITH ORDINALITY{alias}" if offset else alias
    2325        else:
    2326            if isinstance(offset, exp.Expression):
    2327                suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}"
    2328            elif offset:
    2329                suffix = f"{alias} WITH OFFSET"
    2330            else:
    2331                suffix = alias
    2332
    2333        return f"UNNEST({args}){suffix}"

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

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

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

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

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

    2342    def window_sql(self, expression: exp.Window) -> str:
    2343        this = self.sql(expression, "this")
    2344        partition = self.partition_by_sql(expression)
    2345        order = expression.args.get("order")
    2346        order = self.order_sql(order, flat=True) if order else ""
    2347        spec = self.sql(expression, "spec")
    2348        alias = self.sql(expression, "alias")
    2349        over = self.sql(expression, "over") or "OVER"
    2350
    2351        this = f"{this} {'AS' if expression.arg_key == 'windows' else over}"
    2352
    2353        first = expression.args.get("first")
    2354        if first is None:
    2355            first = ""
    2356        else:
    2357            first = "FIRST" if first else "LAST"
    2358
    2359        if not partition and not order and not spec and alias:
    2360            return f"{this} {alias}"
    2361
    2362        args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg)
    2363        return f"{this} ({args})"

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

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

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

    2369    def windowspec_sql(self, expression: exp.WindowSpec) -> str:
    2370        kind = self.sql(expression, "kind")
    2371        start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ")
    2372        end = (
    2373            csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ")
    2374            or "CURRENT ROW"
    2375        )
    2376        return f"{kind} BETWEEN {start} AND {end}"

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

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

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

    2383    def between_sql(self, expression: exp.Between) -> str:
    2384        this = self.sql(expression, "this")
    2385        low = self.sql(expression, "low")
    2386        high = self.sql(expression, "high")
    2387        return f"{this} BETWEEN {low} AND {high}"

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

    2389    def bracket_sql(self, expression: exp.Bracket) -> str:
    2390        expressions = apply_index_offset(
    2391            expression.this,
    2392            expression.expressions,
    2393            self.dialect.INDEX_OFFSET - expression.args.get("offset", 0),
    2394        )
    2395        expressions_sql = ", ".join(self.sql(e) for e in expressions)
    2396        return f"{self.sql(expression, 'this')}[{expressions_sql}]"

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

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

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

    2401    def any_sql(self, expression: exp.Any) -> str:
    2402        this = self.sql(expression, "this")
    2403        if isinstance(expression.this, exp.UNWRAPPED_QUERIES):
    2404            this = self.wrap(this)
    2405        return f"ANY {this}"

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

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

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

    2410    def case_sql(self, expression: exp.Case) -> str:
    2411        this = self.sql(expression, "this")
    2412        statements = [f"CASE {this}" if this else "CASE"]
    2413
    2414        for e in expression.args["ifs"]:
    2415            statements.append(f"WHEN {self.sql(e, 'this')}")
    2416            statements.append(f"THEN {self.sql(e, 'true')}")
    2417
    2418        default = self.sql(expression, "default")
    2419
    2420        if default:
    2421            statements.append(f"ELSE {default}")
    2422
    2423        statements.append("END")
    2424
    2425        if self.pretty and self.text_width(statements) > self.max_text_width:
    2426            return self.indent("\n".join(statements), skip_first=True, skip_last=True)
    2427
    2428        return " ".join(statements)

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

    2430    def constraint_sql(self, expression: exp.Constraint) -> str:
    2431        this = self.sql(expression, "this")
    2432        expressions = self.expressions(expression, flat=True)
    2433        return f"CONSTRAINT {this} {expressions}"

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

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

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

    2440    def extract_sql(self, expression: exp.Extract) -> str:
    2441        this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name
    2442        expression_sql = self.sql(expression, "expression")
    2443        return f"EXTRACT({this} FROM {expression_sql})"

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

    2445    def trim_sql(self, expression: exp.Trim) -> str:
    2446        trim_type = self.sql(expression, "position")
    2447
    2448        if trim_type == "LEADING":
    2449            return self.func("LTRIM", expression.this)
    2450        elif trim_type == "TRAILING":
    2451            return self.func("RTRIM", expression.this)
    2452        else:
    2453            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

    2455    def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]:
    2456        args = expression.expressions
    2457        if isinstance(expression, exp.ConcatWs):
    2458            args = args[1:]  # Skip the delimiter
    2459
    2460        if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
    2461            args = [exp.cast(e, "text") for e in args]
    2462
    2463        if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
    2464            args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args]
    2465
    2466        return args

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

    2468    def concat_sql(self, expression: exp.Concat) -> str:
    2469        expressions = self.convert_concat_args(expression)
    2470
    2471        # Some dialects don't allow a single-argument CONCAT call
    2472        if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1:
    2473            return self.sql(expressions[0])
    2474
    2475        return self.func("CONCAT", *expressions)

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

    2477    def concatws_sql(self, expression: exp.ConcatWs) -> str:
    2478        return self.func(
    2479            "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression)
    2480        )

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

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

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

    2486    def foreignkey_sql(self, expression: exp.ForeignKey) -> str:
    2487        expressions = self.expressions(expression, flat=True)
    2488        reference = self.sql(expression, "reference")
    2489        reference = f" {reference}" if reference else ""
    2490        delete = self.sql(expression, "delete")
    2491        delete = f" ON DELETE {delete}" if delete else ""
    2492        update = self.sql(expression, "update")
    2493        update = f" ON UPDATE {update}" if update else ""
    2494        return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"

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

    2496    def primarykey_sql(self, expression: exp.ForeignKey) -> str:
    2497        expressions = self.expressions(expression, flat=True)
    2498        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2499        options = f" {options}" if options else ""
    2500        return f"PRIMARY KEY ({expressions}){options}"

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

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

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

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

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

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

    2513    def jsonpath_sql(self, expression: exp.JSONPath) -> str:
    2514        path = self.expressions(expression, sep="", flat=True).lstrip(".")
    2515        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

    2517    def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str:
    2518        if isinstance(expression, exp.JSONPathPart):
    2519            transform = self.TRANSFORMS.get(expression.__class__)
    2520            if not callable(transform):
    2521                self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}")
    2522                return ""
    2523
    2524            return transform(self, expression)
    2525
    2526        if isinstance(expression, int):
    2527            return str(expression)
    2528
    2529        if self.JSON_PATH_SINGLE_QUOTE_ESCAPE:
    2530            escaped = expression.replace("'", "\\'")
    2531            escaped = f"\\'{expression}\\'"
    2532        else:
    2533            escaped = expression.replace('"', '\\"')
    2534            escaped = f'"{escaped}"'
    2535
    2536        return escaped

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

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

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

    2541    def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str:
    2542        null_handling = expression.args.get("null_handling")
    2543        null_handling = f" {null_handling}" if null_handling else ""
    2544
    2545        unique_keys = expression.args.get("unique_keys")
    2546        if unique_keys is not None:
    2547            unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS"
    2548        else:
    2549            unique_keys = ""
    2550
    2551        return_type = self.sql(expression, "return_type")
    2552        return_type = f" RETURNING {return_type}" if return_type else ""
    2553        encoding = self.sql(expression, "encoding")
    2554        encoding = f" ENCODING {encoding}" if encoding else ""
    2555
    2556        return self.func(
    2557            "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG",
    2558            *expression.expressions,
    2559            suffix=f"{null_handling}{unique_keys}{return_type}{encoding})",
    2560        )

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

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

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

    2565    def jsonarray_sql(self, expression: exp.JSONArray) -> str:
    2566        null_handling = expression.args.get("null_handling")
    2567        null_handling = f" {null_handling}" if null_handling else ""
    2568        return_type = self.sql(expression, "return_type")
    2569        return_type = f" RETURNING {return_type}" if return_type else ""
    2570        strict = " STRICT" if expression.args.get("strict") else ""
    2571        return self.func(
    2572            "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})"
    2573        )

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

    2575    def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str:
    2576        this = self.sql(expression, "this")
    2577        order = self.sql(expression, "order")
    2578        null_handling = expression.args.get("null_handling")
    2579        null_handling = f" {null_handling}" if null_handling else ""
    2580        return_type = self.sql(expression, "return_type")
    2581        return_type = f" RETURNING {return_type}" if return_type else ""
    2582        strict = " STRICT" if expression.args.get("strict") else ""
    2583        return self.func(
    2584            "JSON_ARRAYAGG",
    2585            this,
    2586            suffix=f"{order}{null_handling}{return_type}{strict})",
    2587        )

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

    2589    def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str:
    2590        path = self.sql(expression, "path")
    2591        path = f" PATH {path}" if path else ""
    2592        nested_schema = self.sql(expression, "nested_schema")
    2593
    2594        if nested_schema:
    2595            return f"NESTED{path} {nested_schema}"
    2596
    2597        this = self.sql(expression, "this")
    2598        kind = self.sql(expression, "kind")
    2599        kind = f" {kind}" if kind else ""
    2600        return f"{this}{kind}{path}"

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

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

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

    2605    def jsontable_sql(self, expression: exp.JSONTable) -> str:
    2606        this = self.sql(expression, "this")
    2607        path = self.sql(expression, "path")
    2608        path = f", {path}" if path else ""
    2609        error_handling = expression.args.get("error_handling")
    2610        error_handling = f" {error_handling}" if error_handling else ""
    2611        empty_handling = expression.args.get("empty_handling")
    2612        empty_handling = f" {empty_handling}" if empty_handling else ""
    2613        schema = self.sql(expression, "schema")
    2614        return self.func(
    2615            "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})"
    2616        )

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

    2618    def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str:
    2619        this = self.sql(expression, "this")
    2620        kind = self.sql(expression, "kind")
    2621        path = self.sql(expression, "path")
    2622        path = f" {path}" if path else ""
    2623        as_json = " AS JSON" if expression.args.get("as_json") else ""
    2624        return f"{this} {kind}{path}{as_json}"

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

    2626    def openjson_sql(self, expression: exp.OpenJSON) -> str:
    2627        this = self.sql(expression, "this")
    2628        path = self.sql(expression, "path")
    2629        path = f", {path}" if path else ""
    2630        expressions = self.expressions(expression)
    2631        with_ = (
    2632            f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}"
    2633            if expressions
    2634            else ""
    2635        )
    2636        return f"OPENJSON({this}{path}){with_}"

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

    2638    def in_sql(self, expression: exp.In) -> str:
    2639        query = expression.args.get("query")
    2640        unnest = expression.args.get("unnest")
    2641        field = expression.args.get("field")
    2642        is_global = " GLOBAL" if expression.args.get("is_global") else ""
    2643
    2644        if query:
    2645            in_sql = self.wrap(self.sql(query))
    2646        elif unnest:
    2647            in_sql = self.in_unnest_op(unnest)
    2648        elif field:
    2649            in_sql = self.sql(field)
    2650        else:
    2651            in_sql = f"({self.expressions(expression, flat=True)})"
    2652
    2653        return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"

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

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

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

    2658    def interval_sql(self, expression: exp.Interval) -> str:
    2659        unit = self.sql(expression, "unit")
    2660        if not self.INTERVAL_ALLOWS_PLURAL_FORM:
    2661            unit = self.TIME_PART_SINGULARS.get(unit, unit)
    2662        unit = f" {unit}" if unit else ""
    2663
    2664        if self.SINGLE_STRING_INTERVAL:
    2665            this = expression.this.name if expression.this else ""
    2666            return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}"
    2667
    2668        this = self.sql(expression, "this")
    2669        if this:
    2670            unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES)
    2671            this = f" {this}" if unwrapped else f" ({this})"
    2672
    2673        return f"INTERVAL{this}{unit}"

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

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

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

    2678    def reference_sql(self, expression: exp.Reference) -> str:
    2679        this = self.sql(expression, "this")
    2680        expressions = self.expressions(expression, flat=True)
    2681        expressions = f"({expressions})" if expressions else ""
    2682        options = self.expressions(expression, key="options", flat=True, sep=" ")
    2683        options = f" {options}" if options else ""
    2684        return f"REFERENCES {this}{expressions}{options}"

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

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

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

    2689    def paren_sql(self, expression: exp.Paren) -> str:
    2690        if isinstance(expression.unnest(), exp.Select):
    2691            sql = self.wrap(expression)
    2692        else:
    2693            sql = self.seg(self.indent(self.sql(expression, "this")), sep="")
    2694            sql = f"({sql}{self.seg(')', sep='')}"
    2695
    2696        return self.prepend_ctes(expression, sql)

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

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

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

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

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

    2707    def alias_sql(self, expression: exp.Alias) -> str:
    2708        alias = self.sql(expression, "alias")
    2709        alias = f" AS {alias}" if alias else ""
    2710        return f"{self.sql(expression, 'this')}{alias}"

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

    2712    def pivotalias_sql(self, expression: exp.PivotAlias) -> str:
    2713        alias = expression.args["alias"]
    2714        identifier_alias = isinstance(alias, exp.Identifier)
    2715
    2716        if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2717            alias.replace(exp.Literal.string(alias.output_name))
    2718        elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS:
    2719            alias.replace(exp.to_identifier(alias.output_name))
    2720
    2721        return self.alias_sql(expression)

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

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

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

    2726    def atindex_sql(self, expression: exp.AtTimeZone) -> str:
    2727        this = self.sql(expression, "this")
    2728        index = self.sql(expression, "expression")
    2729        return f"{this} AT {index}"

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

    2731    def attimezone_sql(self, expression: exp.AtTimeZone) -> str:
    2732        this = self.sql(expression, "this")
    2733        zone = self.sql(expression, "zone")
    2734        return f"{this} AT TIME ZONE {zone}"

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

    2736    def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str:
    2737        this = self.sql(expression, "this")
    2738        zone = self.sql(expression, "zone")
    2739        return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'"

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

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

def and_sql(self, expression: sqlglot.expressions.And) -> str: View Source

    2744    def and_sql(self, expression: exp.And) -> str:
    2745        return self.connector_sql(expression, "AND")

def xor_sql(self, expression: sqlglot.expressions.Xor) -> str: View Source

    2747    def xor_sql(self, expression: exp.Xor) -> str:
    2748        return self.connector_sql(expression, "XOR")

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

    2750    def connector_sql(self, expression: exp.Connector, op: str) -> str:
    2751        if not self.pretty:
    2752            return self.binary(expression, op)
    2753
    2754        sqls = tuple(
    2755            self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e)
    2756            for i, e in enumerate(expression.flatten(unnest=False))
    2757        )
    2758
    2759        sep = "\n" if self.text_width(sqls) > self.max_text_width else " "
    2760        return f"{sep}{op} ".join(sqls)

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

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

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

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

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

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

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

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

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

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

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

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

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

    2780    def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
    2781        format_sql = self.sql(expression, "format")
    2782        format_sql = f" FORMAT {format_sql}" if format_sql else ""
    2783        to_sql = self.sql(expression, "to")
    2784        to_sql = f" {to_sql}" if to_sql else ""
    2785        return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"

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

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

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

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

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

    2794    def collate_sql(self, expression: exp.Collate) -> str:
    2795        if self.COLLATE_IS_FUNC:
    2796            return self.function_fallback_sql(expression)
    2797        return self.binary(expression, "COLLATE")

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

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

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

    2802    def comment_sql(self, expression: exp.Comment) -> str:
    2803        this = self.sql(expression, "this")
    2804        kind = expression.args["kind"]
    2805        exists_sql = " IF EXISTS " if expression.args.get("exists") else " "
    2806        expression_sql = self.sql(expression, "expression")
    2807        return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"

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

    2809    def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str:
    2810        this = self.sql(expression, "this")
    2811        delete = " DELETE" if expression.args.get("delete") else ""
    2812        recompress = self.sql(expression, "recompress")
    2813        recompress = f" RECOMPRESS {recompress}" if recompress else ""
    2814        to_disk = self.sql(expression, "to_disk")
    2815        to_disk = f" TO DISK {to_disk}" if to_disk else ""
    2816        to_volume = self.sql(expression, "to_volume")
    2817        to_volume = f" TO VOLUME {to_volume}" if to_volume else ""
    2818        return f"{this}{delete}{recompress}{to_disk}{to_volume}"

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

    2820    def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str:
    2821        where = self.sql(expression, "where")
    2822        group = self.sql(expression, "group")
    2823        aggregates = self.expressions(expression, key="aggregates")
    2824        aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else ""
    2825
    2826        if not (where or group or aggregates) and len(expression.expressions) == 1:
    2827            return f"TTL {self.expressions(expression, flat=True)}"
    2828
    2829        return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"

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

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

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

    2834    def commit_sql(self, expression: exp.Commit) -> str:
    2835        chain = expression.args.get("chain")
    2836        if chain is not None:
    2837            chain = " AND CHAIN" if chain else " AND NO CHAIN"
    2838
    2839        return f"COMMIT{chain or ''}"

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

    2841    def rollback_sql(self, expression: exp.Rollback) -> str:
    2842        savepoint = expression.args.get("savepoint")
    2843        savepoint = f" TO {savepoint}" if savepoint else ""
    2844        return f"ROLLBACK{savepoint}"

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

    2846    def altercolumn_sql(self, expression: exp.AlterColumn) -> str:
    2847        this = self.sql(expression, "this")
    2848
    2849        dtype = self.sql(expression, "dtype")
    2850        if dtype:
    2851            collate = self.sql(expression, "collate")
    2852            collate = f" COLLATE {collate}" if collate else ""
    2853            using = self.sql(expression, "using")
    2854            using = f" USING {using}" if using else ""
    2855            return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}"
    2856
    2857        default = self.sql(expression, "default")
    2858        if default:
    2859            return f"ALTER COLUMN {this} SET DEFAULT {default}"
    2860
    2861        comment = self.sql(expression, "comment")
    2862        if comment:
    2863            return f"ALTER COLUMN {this} COMMENT {comment}"
    2864
    2865        if not expression.args.get("drop"):
    2866            self.unsupported("Unsupported ALTER COLUMN syntax")
    2867
    2868        return f"ALTER COLUMN {this} DROP DEFAULT"

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

    2870    def renametable_sql(self, expression: exp.RenameTable) -> str:
    2871        if not self.RENAME_TABLE_WITH_DB:
    2872            # Remove db from tables
    2873            expression = expression.transform(
    2874                lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n
    2875            )
    2876        this = self.sql(expression, "this")
    2877        return f"RENAME TO {this}"

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

    2879    def renamecolumn_sql(self, expression: exp.RenameColumn) -> str:
    2880        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2881        old_column = self.sql(expression, "this")
    2882        new_column = self.sql(expression, "to")
    2883        return f"RENAME COLUMN{exists} {old_column} TO {new_column}"

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

    2885    def altertable_sql(self, expression: exp.AlterTable) -> str:
    2886        actions = expression.args["actions"]
    2887
    2888        if isinstance(actions[0], exp.ColumnDef):
    2889            actions = self.add_column_sql(expression)
    2890        elif isinstance(actions[0], exp.Schema):
    2891            actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
    2892        elif isinstance(actions[0], exp.Delete):
    2893            actions = self.expressions(expression, key="actions", flat=True)
    2894        else:
    2895            actions = self.expressions(expression, key="actions", flat=True)
    2896
    2897        exists = " IF EXISTS" if expression.args.get("exists") else ""
    2898        only = " ONLY" if expression.args.get("only") else ""
    2899        options = self.expressions(expression, key="options")
    2900        options = f", {options}" if options else ""
    2901        return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"

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

    2903    def add_column_sql(self, expression: exp.AlterTable) -> str:
    2904        if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD:
    2905            return self.expressions(
    2906                expression,
    2907                key="actions",
    2908                prefix="ADD COLUMN ",
    2909            )
    2910        return f"ADD {self.expressions(expression, key='actions', flat=True)}"

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

    2912    def droppartition_sql(self, expression: exp.DropPartition) -> str:
    2913        expressions = self.expressions(expression)
    2914        exists = " IF EXISTS " if expression.args.get("exists") else " "
    2915        return f"DROP{exists}{expressions}"

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

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

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

    2920    def distinct_sql(self, expression: exp.Distinct) -> str:
    2921        this = self.expressions(expression, flat=True)
    2922
    2923        if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1:
    2924            case = exp.case()
    2925            for arg in expression.expressions:
    2926                case = case.when(arg.is_(exp.null()), exp.null())
    2927            this = self.sql(case.else_(f"({this})"))
    2928
    2929        this = f" {this}" if this else ""
    2930
    2931        on = self.sql(expression, "on")
    2932        on = f" ON {on}" if on else ""
    2933        return f"DISTINCT{this}{on}"

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

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

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

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

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

    2941    def havingmax_sql(self, expression: exp.HavingMax) -> str:
    2942        this_sql = self.sql(expression, "this")
    2943        expression_sql = self.sql(expression, "expression")
    2944        kind = "MAX" if expression.args.get("max") else "MIN"
    2945        return f"{this_sql} HAVING {kind} {expression_sql}"

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

    2971    def intdiv_sql(self, expression: exp.IntDiv) -> str:
    2972        return self.sql(
    2973            exp.Cast(
    2974                this=exp.Div(this=expression.this, expression=expression.expression),
    2975                to=exp.DataType(this=exp.DataType.Type.INT),
    2976            )
    2977        )

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

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

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

    2984    def div_sql(self, expression: exp.Div) -> str:
    2985        l, r = expression.left, expression.right
    2986
    2987        if not self.dialect.SAFE_DIVISION and expression.args.get("safe"):
    2988            r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0)))
    2989
    2990        if self.dialect.TYPED_DIVISION and not expression.args.get("typed"):
    2991            if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES):
    2992                l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE))
    2993
    2994        elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"):
    2995            if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES):
    2996                return self.sql(
    2997                    exp.cast(
    2998                        l / r,
    2999                        to=exp.DataType.Type.BIGINT,
    3000                    )
    3001                )
    3002
    3003        return self.binary(expression, "/")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    3038    def is_sql(self, expression: exp.Is) -> str:
    3039        if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean):
    3040            return self.sql(
    3041                expression.this if expression.expression.this else exp.not_(expression.this)
    3042            )
    3043        return self.binary(expression, "IS")

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

def or_sql(self, expression: sqlglot.expressions.Or) -> str: View Source

    3075    def or_sql(self, expression: exp.Or) -> str:
    3076        return self.connector_sql(expression, "OR")

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

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

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

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

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

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

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

    3087    def log_sql(self, expression: exp.Log) -> str:
    3088        this = expression.this
    3089        expr = expression.expression
    3090
    3091        if not self.dialect.LOG_BASE_FIRST:
    3092            this, expr = expr, this
    3093
    3094        return self.func("LOG", this, expr)

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

    3096    def use_sql(self, expression: exp.Use) -> str:
    3097        kind = self.sql(expression, "kind")
    3098        kind = f" {kind}" if kind else ""
    3099        this = self.sql(expression, "this")
    3100        this = f" {this}" if this else ""
    3101        return f"USE{kind}{this}"

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

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

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

    3107    def function_fallback_sql(self, expression: exp.Func) -> str:
    3108        args = []
    3109
    3110        for key in expression.arg_types:
    3111            arg_value = expression.args.get(key)
    3112
    3113            if isinstance(arg_value, list):
    3114                for value in arg_value:
    3115                    args.append(value)
    3116            elif arg_value is not None:
    3117                args.append(arg_value)
    3118
    3119        if self.normalize_functions:
    3120            name = expression.sql_name()
    3121        else:
    3122            name = (expression._meta and expression.meta.get("name")) or expression.sql_name()
    3123
    3124        return self.func(name, *args)

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

    3126    def func(
    3127        self,
    3128        name: str,
    3129        *args: t.Optional[exp.Expression | str],
    3130        prefix: str = "(",
    3131        suffix: str = ")",
    3132    ) -> str:
    3133        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

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

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

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

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

    3144    def format_time(self, expression: exp.Expression) -> t.Optional[str]:
    3145        return format_time(
    3146            self.sql(expression, "format"),
    3147            self.dialect.INVERSE_TIME_MAPPING,
    3148            self.dialect.INVERSE_TIME_TRIE,
    3149        )

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

    3151    def expressions(
    3152        self,
    3153        expression: t.Optional[exp.Expression] = None,
    3154        key: t.Optional[str] = None,
    3155        sqls: t.Optional[t.Collection[str | exp.Expression]] = None,
    3156        flat: bool = False,
    3157        indent: bool = True,
    3158        skip_first: bool = False,
    3159        sep: str = ", ",
    3160        prefix: str = "",
    3161    ) -> str:
    3162        expressions = expression.args.get(key or "expressions") if expression else sqls
    3163
    3164        if not expressions:
    3165            return ""
    3166
    3167        if flat:
    3168            return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql)
    3169
    3170        num_sqls = len(expressions)
    3171
    3172        # These are calculated once in case we have the leading_comma / pretty option set, correspondingly
    3173        pad = " " * self.pad
    3174        stripped_sep = sep.strip()
    3175
    3176        result_sqls = []
    3177        for i, e in enumerate(expressions):
    3178            sql = self.sql(e, comment=False)
    3179            if not sql:
    3180                continue
    3181
    3182            comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else ""
    3183
    3184            if self.pretty:
    3185                if self.leading_comma:
    3186                    result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}")
    3187                else:
    3188                    result_sqls.append(
    3189                        f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}"
    3190                    )
    3191            else:
    3192                result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}")
    3193
    3194        result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls)
    3195        return self.indent(result_sql, skip_first=skip_first) if indent else result_sql

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

    3197    def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str:
    3198        flat = flat or isinstance(expression.parent, exp.Properties)
    3199        expressions_sql = self.expressions(expression, flat=flat)
    3200        if flat:
    3201            return f"{op} {expressions_sql}"
    3202        return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"

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

    3204    def naked_property(self, expression: exp.Property) -> str:
    3205        property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__)
    3206        if not property_name:
    3207            self.unsupported(f"Unsupported property {expression.__class__.__name__}")
    3208        return f"{property_name} {self.sql(expression, 'this')}"

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

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

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

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

    3216    def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str:
    3217        this = self.sql(expression, "this")
    3218        expressions = self.no_identify(self.expressions, expression)
    3219        expressions = (
    3220            self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}"
    3221        )
    3222        return f"{this}{expressions}"

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

    3224    def joinhint_sql(self, expression: exp.JoinHint) -> str:
    3225        this = self.sql(expression, "this")
    3226        expressions = self.expressions(expression, flat=True)
    3227        return f"{this}({expressions})"

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

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

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

    3232    def when_sql(self, expression: exp.When) -> str:
    3233        matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
    3234        source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else ""
    3235        condition = self.sql(expression, "condition")
    3236        condition = f" AND {condition}" if condition else ""
    3237
    3238        then_expression = expression.args.get("then")
    3239        if isinstance(then_expression, exp.Insert):
    3240            then = f"INSERT {self.sql(then_expression, 'this')}"
    3241            if "expression" in then_expression.args:
    3242                then += f" VALUES {self.sql(then_expression, 'expression')}"
    3243        elif isinstance(then_expression, exp.Update):
    3244            if isinstance(then_expression.args.get("expressions"), exp.Star):
    3245                then = f"UPDATE {self.sql(then_expression, 'expressions')}"
    3246            else:
    3247                then = f"UPDATE SET {self.expressions(then_expression, flat=True)}"
    3248        else:
    3249            then = self.sql(then_expression)
    3250        return f"WHEN {matched}{source}{condition} THEN {then}"

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

    3252    def merge_sql(self, expression: exp.Merge) -> str:
    3253        table = expression.this
    3254        table_alias = ""
    3255
    3256        hints = table.args.get("hints")
    3257        if hints and table.alias and isinstance(hints[0], exp.WithTableHint):
    3258            # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias]
    3259            table_alias = f" AS {self.sql(table.args['alias'].pop())}"
    3260
    3261        this = self.sql(table)
    3262        using = f"USING {self.sql(expression, 'using')}"
    3263        on = f"ON {self.sql(expression, 'on')}"
    3264        expressions = self.expressions(expression, sep=" ")
    3265
    3266        return self.prepend_ctes(
    3267            expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}"
    3268        )

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

    3270    def tochar_sql(self, expression: exp.ToChar) -> str:
    3271        if expression.args.get("format"):
    3272            self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function")
    3273
    3274        return self.sql(exp.cast(expression.this, "text"))

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

    3276    def dictproperty_sql(self, expression: exp.DictProperty) -> str:
    3277        this = self.sql(expression, "this")
    3278        kind = self.sql(expression, "kind")
    3279        settings_sql = self.expressions(expression, key="settings", sep=" ")
    3280        args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()"
    3281        return f"{this}({kind}{args})"

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

    3283    def dictrange_sql(self, expression: exp.DictRange) -> str:
    3284        this = self.sql(expression, "this")
    3285        max = self.sql(expression, "max")
    3286        min = self.sql(expression, "min")
    3287        return f"{this}(MIN {min} MAX {max})"

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

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

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

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

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

    3295    def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str:
    3296        expressions = self.expressions(expression, key="expressions", flat=True)
    3297        sorted_by = self.expressions(expression, key="sorted_by", flat=True)
    3298        sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else ""
    3299        buckets = self.sql(expression, "buckets")
    3300        return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"

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

    3302    def anyvalue_sql(self, expression: exp.AnyValue) -> str:
    3303        this = self.sql(expression, "this")
    3304        having = self.sql(expression, "having")
    3305
    3306        if having:
    3307            this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}"
    3308
    3309        return self.func("ANY_VALUE", this)

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

    3311    def querytransform_sql(self, expression: exp.QueryTransform) -> str:
    3312        transform = self.func("TRANSFORM", *expression.expressions)
    3313        row_format_before = self.sql(expression, "row_format_before")
    3314        row_format_before = f" {row_format_before}" if row_format_before else ""
    3315        record_writer = self.sql(expression, "record_writer")
    3316        record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
    3317        using = f" USING {self.sql(expression, 'command_script')}"
    3318        schema = self.sql(expression, "schema")
    3319        schema = f" AS {schema}" if schema else ""
    3320        row_format_after = self.sql(expression, "row_format_after")
    3321        row_format_after = f" {row_format_after}" if row_format_after else ""
    3322        record_reader = self.sql(expression, "record_reader")
    3323        record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
    3324        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

    3326    def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str:
    3327        key_block_size = self.sql(expression, "key_block_size")
    3328        if key_block_size:
    3329            return f"KEY_BLOCK_SIZE = {key_block_size}"
    3330
    3331        using = self.sql(expression, "using")
    3332        if using:
    3333            return f"USING {using}"
    3334
    3335        parser = self.sql(expression, "parser")
    3336        if parser:
    3337            return f"WITH PARSER {parser}"
    3338
    3339        comment = self.sql(expression, "comment")
    3340        if comment:
    3341            return f"COMMENT {comment}"
    3342
    3343        visible = expression.args.get("visible")
    3344        if visible is not None:
    3345            return "VISIBLE" if visible else "INVISIBLE"
    3346
    3347        engine_attr = self.sql(expression, "engine_attr")
    3348        if engine_attr:
    3349            return f"ENGINE_ATTRIBUTE = {engine_attr}"
    3350
    3351        secondary_engine_attr = self.sql(expression, "secondary_engine_attr")
    3352        if secondary_engine_attr:
    3353            return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}"
    3354
    3355        self.unsupported("Unsupported index constraint option.")
    3356        return ""

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

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

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

    3362    def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str:
    3363        kind = self.sql(expression, "kind")
    3364        kind = f"{kind} INDEX" if kind else "INDEX"
    3365        this = self.sql(expression, "this")
    3366        this = f" {this}" if this else ""
    3367        index_type = self.sql(expression, "index_type")
    3368        index_type = f" USING {index_type}" if index_type else ""
    3369        schema = self.sql(expression, "schema")
    3370        schema = f" {schema}" if schema else ""
    3371        options = self.expressions(expression, key="options", sep=" ")
    3372        options = f" {options}" if options else ""
    3373        return f"{kind}{this}{index_type}{schema}{options}"

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

    3375    def nvl2_sql(self, expression: exp.Nvl2) -> str:
    3376        if self.NVL2_SUPPORTED:
    3377            return self.function_fallback_sql(expression)
    3378
    3379        case = exp.Case().when(
    3380            expression.this.is_(exp.null()).not_(copy=False),
    3381            expression.args["true"],
    3382            copy=False,
    3383        )
    3384        else_cond = expression.args.get("false")
    3385        if else_cond:
    3386            case.else_(else_cond, copy=False)
    3387
    3388        return self.sql(case)

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

    3390    def comprehension_sql(self, expression: exp.Comprehension) -> str:
    3391        this = self.sql(expression, "this")
    3392        expr = self.sql(expression, "expression")
    3393        iterator = self.sql(expression, "iterator")
    3394        condition = self.sql(expression, "condition")
    3395        condition = f" IF {condition}" if condition else ""
    3396        return f"{this} FOR {expr} IN {iterator}{condition}"

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

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

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

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

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

    3404    def predict_sql(self, expression: exp.Predict) -> str:
    3405        model = self.sql(expression, "this")
    3406        model = f"MODEL {model}"
    3407        table = self.sql(expression, "expression")
    3408        table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table
    3409        parameters = self.sql(expression, "params_struct")
    3410        return self.func("PREDICT", model, table, parameters or None)

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

    3412    def forin_sql(self, expression: exp.ForIn) -> str:
    3413        this = self.sql(expression, "this")
    3414        expression_sql = self.sql(expression, "expression")
    3415        return f"FOR {this} DO {expression_sql}"

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

    3417    def refresh_sql(self, expression: exp.Refresh) -> str:
    3418        this = self.sql(expression, "this")
    3419        table = "" if isinstance(expression.this, exp.Literal) else "TABLE "
    3420        return f"REFRESH {table}{this}"

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

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

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

    3425    def toarray_sql(self, expression: exp.ToArray) -> str:
    3426        arg = expression.this
    3427        if not arg.type:
    3428            from sqlglot.optimizer.annotate_types import annotate_types
    3429
    3430            arg = annotate_types(arg)
    3431
    3432        if arg.is_type(exp.DataType.Type.ARRAY):
    3433            return self.sql(arg)
    3434
    3435        cond_for_null = arg.is_(exp.null())
    3436        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

    3438    def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str:
    3439        this = expression.this
    3440        if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME):
    3441            return self.sql(this)
    3442
    3443        return self.sql(exp.cast(this, "time"))

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

    3445    def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str:
    3446        this = expression.this
    3447        time_format = self.format_time(expression)
    3448
    3449        if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT):
    3450            return self.sql(
    3451                exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date")
    3452            )
    3453
    3454        if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE):
    3455            return self.sql(this)
    3456
    3457        return self.sql(exp.cast(this, "date"))

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

    3459    def unixdate_sql(self, expression: exp.UnixDate) -> str:
    3460        return self.sql(
    3461            exp.func(
    3462                "DATEDIFF",
    3463                expression.this,
    3464                exp.cast(exp.Literal.string("1970-01-01"), "date"),
    3465                "day",
    3466            )
    3467        )

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

    3469    def lastday_sql(self, expression: exp.LastDay) -> str:
    3470        if self.LAST_DAY_SUPPORTS_DATE_PART:
    3471            return self.function_fallback_sql(expression)
    3472
    3473        unit = expression.text("unit")
    3474        if unit and unit != "MONTH":
    3475            self.unsupported("Date parts are not supported in LAST_DAY.")
    3476
    3477        return self.func("LAST_DAY", expression.this)

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

    3479    def arrayany_sql(self, expression: exp.ArrayAny) -> str:
    3480        if self.CAN_IMPLEMENT_ARRAY_ANY:
    3481            filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression)
    3482            filtered_not_empty = exp.ArraySize(this=filtered).neq(0)
    3483            original_is_empty = exp.ArraySize(this=expression.this).eq(0)
    3484            return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty)))
    3485
    3486        from sqlglot.dialects import Dialect
    3487
    3488        # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect
    3489        if self.dialect.__class__ != Dialect:
    3490            self.unsupported("ARRAY_ANY is unsupported")
    3491
    3492        return self.function_fallback_sql(expression)

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

    3518    def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
    3519        expression.set("is_end_exclusive", None)
    3520        return self.function_fallback_sql(expression)

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

    3522    def struct_sql(self, expression: exp.Struct) -> str:
    3523        expression.set(
    3524            "expressions",
    3525            [
    3526                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
    3527                for e in expression.expressions
    3528            ],
    3529        )
    3530
    3531        return self.function_fallback_sql(expression)

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

    3533    def partitionrange_sql(self, expression: exp.PartitionRange) -> str:
    3534        low = self.sql(expression, "this")
    3535        high = self.sql(expression, "expression")
    3536
    3537        return f"{low} TO {high}"

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

    3539    def truncatetable_sql(self, expression: exp.TruncateTable) -> str:
    3540        target = "DATABASE" if expression.args.get("is_database") else "TABLE"
    3541        tables = f" {self.expressions(expression)}"
    3542
    3543        exists = " IF EXISTS" if expression.args.get("exists") else ""
    3544
    3545        on_cluster = self.sql(expression, "cluster")
    3546        on_cluster = f" {on_cluster}" if on_cluster else ""
    3547
    3548        identity = self.sql(expression, "identity")
    3549        identity = f" {identity} IDENTITY" if identity else ""
    3550
    3551        option = self.sql(expression, "option")
    3552        option = f" {option}" if option else ""
    3553
    3554        partition = self.sql(expression, "partition")
    3555        partition = f" {partition}" if partition else ""
    3556
    3557        return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"

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

    3561    def convert_sql(self, expression: exp.Convert) -> str:
    3562        to = expression.this
    3563        value = expression.expression
    3564        style = expression.args.get("style")
    3565        safe = expression.args.get("safe")
    3566        strict = expression.args.get("strict")
    3567
    3568        if not to or not value:
    3569            return ""
    3570
    3571        # Retrieve length of datatype and override to default if not specified
    3572        if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3573            to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False)
    3574
    3575        transformed: t.Optional[exp.Expression] = None
    3576        cast = exp.Cast if strict else exp.TryCast
    3577
    3578        # Check whether a conversion with format (T-SQL calls this 'style') is applicable
    3579        if isinstance(style, exp.Literal) and style.is_int:
    3580            from sqlglot.dialects.tsql import TSQL
    3581
    3582            style_value = style.name
    3583            converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value)
    3584            if not converted_style:
    3585                self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}")
    3586
    3587            fmt = exp.Literal.string(converted_style)
    3588
    3589            if to.this == exp.DataType.Type.DATE:
    3590                transformed = exp.StrToDate(this=value, format=fmt)
    3591            elif to.this == exp.DataType.Type.DATETIME:
    3592                transformed = exp.StrToTime(this=value, format=fmt)
    3593            elif to.this in self.PARAMETERIZABLE_TEXT_TYPES:
    3594                transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe)
    3595            elif to.this == exp.DataType.Type.TEXT:
    3596                transformed = exp.TimeToStr(this=value, format=fmt)
    3597
    3598        if not transformed:
    3599            transformed = cast(this=value, to=to, safe=safe)
    3600
    3601        return self.sql(transformed)