Edit on GitHub

sqlglot.parser

   1from __future__ import annotations
   2
   3import logging
   4import typing as t
   5import itertools
   6from collections import defaultdict
   7
   8from sqlglot import exp
   9from sqlglot.errors import ErrorLevel, ParseError, concat_messages, merge_errors
  10from sqlglot.helper import apply_index_offset, ensure_list, seq_get
  11from sqlglot.time import format_time
  12from sqlglot.tokens import Token, Tokenizer, TokenType
  13from sqlglot.trie import TrieResult, in_trie, new_trie
  14
  15if t.TYPE_CHECKING:
  16    from sqlglot._typing import E, Lit
  17    from sqlglot.dialects.dialect import Dialect, DialectType
  18
  19    T = t.TypeVar("T")
  20    TCeilFloor = t.TypeVar("TCeilFloor", exp.Ceil, exp.Floor)
  21
  22logger = logging.getLogger("sqlglot")
  23
  24OPTIONS_TYPE = t.Dict[str, t.Sequence[t.Union[t.Sequence[str], str]]]
  25
  26
  27def build_var_map(args: t.List) -> exp.StarMap | exp.VarMap:
  28    if len(args) == 1 and args[0].is_star:
  29        return exp.StarMap(this=args[0])
  30
  31    keys = []
  32    values = []
  33    for i in range(0, len(args), 2):
  34        keys.append(args[i])
  35        values.append(args[i + 1])
  36
  37    return exp.VarMap(keys=exp.array(*keys, copy=False), values=exp.array(*values, copy=False))
  38
  39
  40def build_like(args: t.List) -> exp.Escape | exp.Like:
  41    like = exp.Like(this=seq_get(args, 1), expression=seq_get(args, 0))
  42    return exp.Escape(this=like, expression=seq_get(args, 2)) if len(args) > 2 else like
  43
  44
  45def binary_range_parser(
  46    expr_type: t.Type[exp.Expression], reverse_args: bool = False
  47) -> t.Callable[[Parser, t.Optional[exp.Expression]], t.Optional[exp.Expression]]:
  48    def _parse_binary_range(
  49        self: Parser, this: t.Optional[exp.Expression]
  50    ) -> t.Optional[exp.Expression]:
  51        expression = self._parse_bitwise()
  52        if reverse_args:
  53            this, expression = expression, this
  54        return self._parse_escape(self.expression(expr_type, this=this, expression=expression))
  55
  56    return _parse_binary_range
  57
  58
  59def build_logarithm(args: t.List, dialect: Dialect) -> exp.Func:
  60    # Default argument order is base, expression
  61    this = seq_get(args, 0)
  62    expression = seq_get(args, 1)
  63
  64    if expression:
  65        if not dialect.LOG_BASE_FIRST:
  66            this, expression = expression, this
  67        return exp.Log(this=this, expression=expression)
  68
  69    return (exp.Ln if dialect.parser_class.LOG_DEFAULTS_TO_LN else exp.Log)(this=this)
  70
  71
  72def build_hex(args: t.List, dialect: Dialect) -> exp.Hex | exp.LowerHex:
  73    arg = seq_get(args, 0)
  74    return exp.LowerHex(this=arg) if dialect.HEX_LOWERCASE else exp.Hex(this=arg)
  75
  76
  77def build_lower(args: t.List) -> exp.Lower | exp.Hex:
  78    # LOWER(HEX(..)) can be simplified to LowerHex to simplify its transpilation
  79    arg = seq_get(args, 0)
  80    return exp.LowerHex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Lower(this=arg)
  81
  82
  83def build_upper(args: t.List) -> exp.Upper | exp.Hex:
  84    # UPPER(HEX(..)) can be simplified to Hex to simplify its transpilation
  85    arg = seq_get(args, 0)
  86    return exp.Hex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Upper(this=arg)
  87
  88
  89def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
  90    def _builder(args: t.List, dialect: Dialect) -> E:
  91        expression = expr_type(
  92            this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
  93        )
  94        if len(args) > 2 and expr_type is exp.JSONExtract:
  95            expression.set("expressions", args[2:])
  96
  97        return expression
  98
  99    return _builder
 100
 101
 102def build_mod(args: t.List) -> exp.Mod:
 103    this = seq_get(args, 0)
 104    expression = seq_get(args, 1)
 105
 106    # Wrap the operands if they are binary nodes, e.g. MOD(a + 1, 7) -> (a + 1) % 7
 107    this = exp.Paren(this=this) if isinstance(this, exp.Binary) else this
 108    expression = exp.Paren(this=expression) if isinstance(expression, exp.Binary) else expression
 109
 110    return exp.Mod(this=this, expression=expression)
 111
 112
 113def build_pad(args: t.List, is_left: bool = True):
 114    return exp.Pad(
 115        this=seq_get(args, 0),
 116        expression=seq_get(args, 1),
 117        fill_pattern=seq_get(args, 2),
 118        is_left=is_left,
 119    )
 120
 121
 122def build_array_constructor(
 123    exp_class: t.Type[E], args: t.List, bracket_kind: TokenType, dialect: Dialect
 124) -> exp.Expression:
 125    array_exp = exp_class(expressions=args)
 126
 127    if exp_class == exp.Array and dialect.HAS_DISTINCT_ARRAY_CONSTRUCTORS:
 128        array_exp.set("bracket_notation", bracket_kind == TokenType.L_BRACKET)
 129
 130    return array_exp
 131
 132
 133def build_convert_timezone(
 134    args: t.List, default_source_tz: t.Optional[str] = None
 135) -> t.Union[exp.ConvertTimezone, exp.Anonymous]:
 136    if len(args) == 2:
 137        source_tz = exp.Literal.string(default_source_tz) if default_source_tz else None
 138        return exp.ConvertTimezone(
 139            source_tz=source_tz, target_tz=seq_get(args, 0), timestamp=seq_get(args, 1)
 140        )
 141
 142    return exp.ConvertTimezone.from_arg_list(args)
 143
 144
 145def build_trim(args: t.List, is_left: bool = True):
 146    return exp.Trim(
 147        this=seq_get(args, 0),
 148        expression=seq_get(args, 1),
 149        position="LEADING" if is_left else "TRAILING",
 150    )
 151
 152
 153def build_coalesce(args: t.List, is_nvl: t.Optional[bool] = None) -> exp.Coalesce:
 154    return exp.Coalesce(this=seq_get(args, 0), expressions=args[1:], is_nvl=is_nvl)
 155
 156
 157def build_locate_strposition(args: t.List):
 158    return exp.StrPosition(
 159        this=seq_get(args, 1),
 160        substr=seq_get(args, 0),
 161        position=seq_get(args, 2),
 162    )
 163
 164
 165class _Parser(type):
 166    def __new__(cls, clsname, bases, attrs):
 167        klass = super().__new__(cls, clsname, bases, attrs)
 168
 169        klass.SHOW_TRIE = new_trie(key.split(" ") for key in klass.SHOW_PARSERS)
 170        klass.SET_TRIE = new_trie(key.split(" ") for key in klass.SET_PARSERS)
 171
 172        return klass
 173
 174
 175class Parser(metaclass=_Parser):
 176    """
 177    Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.
 178
 179    Args:
 180        error_level: The desired error level.
 181            Default: ErrorLevel.IMMEDIATE
 182        error_message_context: The amount of context to capture from a query string when displaying
 183            the error message (in number of characters).
 184            Default: 100
 185        max_errors: Maximum number of error messages to include in a raised ParseError.
 186            This is only relevant if error_level is ErrorLevel.RAISE.
 187            Default: 3
 188    """
 189
 190    FUNCTIONS: t.Dict[str, t.Callable] = {
 191        **{name: func.from_arg_list for name, func in exp.FUNCTION_BY_NAME.items()},
 192        **dict.fromkeys(("COALESCE", "IFNULL", "NVL"), build_coalesce),
 193        "ARRAY": lambda args, dialect: exp.Array(expressions=args),
 194        "ARRAYAGG": lambda args, dialect: exp.ArrayAgg(
 195            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 196        ),
 197        "ARRAY_AGG": lambda args, dialect: exp.ArrayAgg(
 198            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 199        ),
 200        "CHAR": lambda args: exp.Chr(expressions=args),
 201        "CHR": lambda args: exp.Chr(expressions=args),
 202        "COUNT": lambda args: exp.Count(this=seq_get(args, 0), expressions=args[1:], big_int=True),
 203        "CONCAT": lambda args, dialect: exp.Concat(
 204            expressions=args,
 205            safe=not dialect.STRICT_STRING_CONCAT,
 206            coalesce=dialect.CONCAT_COALESCE,
 207        ),
 208        "CONCAT_WS": lambda args, dialect: exp.ConcatWs(
 209            expressions=args,
 210            safe=not dialect.STRICT_STRING_CONCAT,
 211            coalesce=dialect.CONCAT_COALESCE,
 212        ),
 213        "CONVERT_TIMEZONE": build_convert_timezone,
 214        "DATE_TO_DATE_STR": lambda args: exp.Cast(
 215            this=seq_get(args, 0),
 216            to=exp.DataType(this=exp.DataType.Type.TEXT),
 217        ),
 218        "GENERATE_DATE_ARRAY": lambda args: exp.GenerateDateArray(
 219            start=seq_get(args, 0),
 220            end=seq_get(args, 1),
 221            step=seq_get(args, 2) or exp.Interval(this=exp.Literal.string(1), unit=exp.var("DAY")),
 222        ),
 223        "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)),
 224        "HEX": build_hex,
 225        "JSON_EXTRACT": build_extract_json_with_path(exp.JSONExtract),
 226        "JSON_EXTRACT_SCALAR": build_extract_json_with_path(exp.JSONExtractScalar),
 227        "JSON_EXTRACT_PATH_TEXT": build_extract_json_with_path(exp.JSONExtractScalar),
 228        "LIKE": build_like,
 229        "LOG": build_logarithm,
 230        "LOG2": lambda args: exp.Log(this=exp.Literal.number(2), expression=seq_get(args, 0)),
 231        "LOG10": lambda args: exp.Log(this=exp.Literal.number(10), expression=seq_get(args, 0)),
 232        "LOWER": build_lower,
 233        "LPAD": lambda args: build_pad(args),
 234        "LEFTPAD": lambda args: build_pad(args),
 235        "LTRIM": lambda args: build_trim(args),
 236        "MOD": build_mod,
 237        "RIGHTPAD": lambda args: build_pad(args, is_left=False),
 238        "RPAD": lambda args: build_pad(args, is_left=False),
 239        "RTRIM": lambda args: build_trim(args, is_left=False),
 240        "SCOPE_RESOLUTION": lambda args: exp.ScopeResolution(expression=seq_get(args, 0))
 241        if len(args) != 2
 242        else exp.ScopeResolution(this=seq_get(args, 0), expression=seq_get(args, 1)),
 243        "STRPOS": exp.StrPosition.from_arg_list,
 244        "CHARINDEX": lambda args: build_locate_strposition(args),
 245        "INSTR": exp.StrPosition.from_arg_list,
 246        "LOCATE": lambda args: build_locate_strposition(args),
 247        "TIME_TO_TIME_STR": lambda args: exp.Cast(
 248            this=seq_get(args, 0),
 249            to=exp.DataType(this=exp.DataType.Type.TEXT),
 250        ),
 251        "TO_HEX": build_hex,
 252        "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring(
 253            this=exp.Cast(
 254                this=seq_get(args, 0),
 255                to=exp.DataType(this=exp.DataType.Type.TEXT),
 256            ),
 257            start=exp.Literal.number(1),
 258            length=exp.Literal.number(10),
 259        ),
 260        "UNNEST": lambda args: exp.Unnest(expressions=ensure_list(seq_get(args, 0))),
 261        "UPPER": build_upper,
 262        "VAR_MAP": build_var_map,
 263    }
 264
 265    NO_PAREN_FUNCTIONS = {
 266        TokenType.CURRENT_DATE: exp.CurrentDate,
 267        TokenType.CURRENT_DATETIME: exp.CurrentDate,
 268        TokenType.CURRENT_TIME: exp.CurrentTime,
 269        TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp,
 270        TokenType.CURRENT_USER: exp.CurrentUser,
 271    }
 272
 273    STRUCT_TYPE_TOKENS = {
 274        TokenType.NESTED,
 275        TokenType.OBJECT,
 276        TokenType.STRUCT,
 277        TokenType.UNION,
 278    }
 279
 280    NESTED_TYPE_TOKENS = {
 281        TokenType.ARRAY,
 282        TokenType.LIST,
 283        TokenType.LOWCARDINALITY,
 284        TokenType.MAP,
 285        TokenType.NULLABLE,
 286        TokenType.RANGE,
 287        *STRUCT_TYPE_TOKENS,
 288    }
 289
 290    ENUM_TYPE_TOKENS = {
 291        TokenType.DYNAMIC,
 292        TokenType.ENUM,
 293        TokenType.ENUM8,
 294        TokenType.ENUM16,
 295    }
 296
 297    AGGREGATE_TYPE_TOKENS = {
 298        TokenType.AGGREGATEFUNCTION,
 299        TokenType.SIMPLEAGGREGATEFUNCTION,
 300    }
 301
 302    TYPE_TOKENS = {
 303        TokenType.BIT,
 304        TokenType.BOOLEAN,
 305        TokenType.TINYINT,
 306        TokenType.UTINYINT,
 307        TokenType.SMALLINT,
 308        TokenType.USMALLINT,
 309        TokenType.INT,
 310        TokenType.UINT,
 311        TokenType.BIGINT,
 312        TokenType.UBIGINT,
 313        TokenType.INT128,
 314        TokenType.UINT128,
 315        TokenType.INT256,
 316        TokenType.UINT256,
 317        TokenType.MEDIUMINT,
 318        TokenType.UMEDIUMINT,
 319        TokenType.FIXEDSTRING,
 320        TokenType.FLOAT,
 321        TokenType.DOUBLE,
 322        TokenType.UDOUBLE,
 323        TokenType.CHAR,
 324        TokenType.NCHAR,
 325        TokenType.VARCHAR,
 326        TokenType.NVARCHAR,
 327        TokenType.BPCHAR,
 328        TokenType.TEXT,
 329        TokenType.MEDIUMTEXT,
 330        TokenType.LONGTEXT,
 331        TokenType.BLOB,
 332        TokenType.MEDIUMBLOB,
 333        TokenType.LONGBLOB,
 334        TokenType.BINARY,
 335        TokenType.VARBINARY,
 336        TokenType.JSON,
 337        TokenType.JSONB,
 338        TokenType.INTERVAL,
 339        TokenType.TINYBLOB,
 340        TokenType.TINYTEXT,
 341        TokenType.TIME,
 342        TokenType.TIMETZ,
 343        TokenType.TIMESTAMP,
 344        TokenType.TIMESTAMP_S,
 345        TokenType.TIMESTAMP_MS,
 346        TokenType.TIMESTAMP_NS,
 347        TokenType.TIMESTAMPTZ,
 348        TokenType.TIMESTAMPLTZ,
 349        TokenType.TIMESTAMPNTZ,
 350        TokenType.DATETIME,
 351        TokenType.DATETIME2,
 352        TokenType.DATETIME64,
 353        TokenType.SMALLDATETIME,
 354        TokenType.DATE,
 355        TokenType.DATE32,
 356        TokenType.INT4RANGE,
 357        TokenType.INT4MULTIRANGE,
 358        TokenType.INT8RANGE,
 359        TokenType.INT8MULTIRANGE,
 360        TokenType.NUMRANGE,
 361        TokenType.NUMMULTIRANGE,
 362        TokenType.TSRANGE,
 363        TokenType.TSMULTIRANGE,
 364        TokenType.TSTZRANGE,
 365        TokenType.TSTZMULTIRANGE,
 366        TokenType.DATERANGE,
 367        TokenType.DATEMULTIRANGE,
 368        TokenType.DECIMAL,
 369        TokenType.DECIMAL32,
 370        TokenType.DECIMAL64,
 371        TokenType.DECIMAL128,
 372        TokenType.DECIMAL256,
 373        TokenType.UDECIMAL,
 374        TokenType.BIGDECIMAL,
 375        TokenType.UUID,
 376        TokenType.GEOGRAPHY,
 377        TokenType.GEOMETRY,
 378        TokenType.POINT,
 379        TokenType.RING,
 380        TokenType.LINESTRING,
 381        TokenType.MULTILINESTRING,
 382        TokenType.POLYGON,
 383        TokenType.MULTIPOLYGON,
 384        TokenType.HLLSKETCH,
 385        TokenType.HSTORE,
 386        TokenType.PSEUDO_TYPE,
 387        TokenType.SUPER,
 388        TokenType.SERIAL,
 389        TokenType.SMALLSERIAL,
 390        TokenType.BIGSERIAL,
 391        TokenType.XML,
 392        TokenType.YEAR,
 393        TokenType.USERDEFINED,
 394        TokenType.MONEY,
 395        TokenType.SMALLMONEY,
 396        TokenType.ROWVERSION,
 397        TokenType.IMAGE,
 398        TokenType.VARIANT,
 399        TokenType.VECTOR,
 400        TokenType.VOID,
 401        TokenType.OBJECT,
 402        TokenType.OBJECT_IDENTIFIER,
 403        TokenType.INET,
 404        TokenType.IPADDRESS,
 405        TokenType.IPPREFIX,
 406        TokenType.IPV4,
 407        TokenType.IPV6,
 408        TokenType.UNKNOWN,
 409        TokenType.NOTHING,
 410        TokenType.NULL,
 411        TokenType.NAME,
 412        TokenType.TDIGEST,
 413        TokenType.DYNAMIC,
 414        *ENUM_TYPE_TOKENS,
 415        *NESTED_TYPE_TOKENS,
 416        *AGGREGATE_TYPE_TOKENS,
 417    }
 418
 419    SIGNED_TO_UNSIGNED_TYPE_TOKEN = {
 420        TokenType.BIGINT: TokenType.UBIGINT,
 421        TokenType.INT: TokenType.UINT,
 422        TokenType.MEDIUMINT: TokenType.UMEDIUMINT,
 423        TokenType.SMALLINT: TokenType.USMALLINT,
 424        TokenType.TINYINT: TokenType.UTINYINT,
 425        TokenType.DECIMAL: TokenType.UDECIMAL,
 426        TokenType.DOUBLE: TokenType.UDOUBLE,
 427    }
 428
 429    SUBQUERY_PREDICATES = {
 430        TokenType.ANY: exp.Any,
 431        TokenType.ALL: exp.All,
 432        TokenType.EXISTS: exp.Exists,
 433        TokenType.SOME: exp.Any,
 434    }
 435
 436    RESERVED_TOKENS = {
 437        *Tokenizer.SINGLE_TOKENS.values(),
 438        TokenType.SELECT,
 439    } - {TokenType.IDENTIFIER}
 440
 441    DB_CREATABLES = {
 442        TokenType.DATABASE,
 443        TokenType.DICTIONARY,
 444        TokenType.FILE_FORMAT,
 445        TokenType.MODEL,
 446        TokenType.NAMESPACE,
 447        TokenType.SCHEMA,
 448        TokenType.SEQUENCE,
 449        TokenType.SINK,
 450        TokenType.SOURCE,
 451        TokenType.STAGE,
 452        TokenType.STORAGE_INTEGRATION,
 453        TokenType.STREAMLIT,
 454        TokenType.TABLE,
 455        TokenType.TAG,
 456        TokenType.VIEW,
 457        TokenType.WAREHOUSE,
 458    }
 459
 460    CREATABLES = {
 461        TokenType.COLUMN,
 462        TokenType.CONSTRAINT,
 463        TokenType.FOREIGN_KEY,
 464        TokenType.FUNCTION,
 465        TokenType.INDEX,
 466        TokenType.PROCEDURE,
 467        *DB_CREATABLES,
 468    }
 469
 470    ALTERABLES = {
 471        TokenType.INDEX,
 472        TokenType.TABLE,
 473        TokenType.VIEW,
 474    }
 475
 476    # Tokens that can represent identifiers
 477    ID_VAR_TOKENS = {
 478        TokenType.ALL,
 479        TokenType.ATTACH,
 480        TokenType.VAR,
 481        TokenType.ANTI,
 482        TokenType.APPLY,
 483        TokenType.ASC,
 484        TokenType.ASOF,
 485        TokenType.AUTO_INCREMENT,
 486        TokenType.BEGIN,
 487        TokenType.BPCHAR,
 488        TokenType.CACHE,
 489        TokenType.CASE,
 490        TokenType.COLLATE,
 491        TokenType.COMMAND,
 492        TokenType.COMMENT,
 493        TokenType.COMMIT,
 494        TokenType.CONSTRAINT,
 495        TokenType.COPY,
 496        TokenType.CUBE,
 497        TokenType.CURRENT_SCHEMA,
 498        TokenType.DEFAULT,
 499        TokenType.DELETE,
 500        TokenType.DESC,
 501        TokenType.DESCRIBE,
 502        TokenType.DETACH,
 503        TokenType.DICTIONARY,
 504        TokenType.DIV,
 505        TokenType.END,
 506        TokenType.EXECUTE,
 507        TokenType.EXPORT,
 508        TokenType.ESCAPE,
 509        TokenType.FALSE,
 510        TokenType.FIRST,
 511        TokenType.FILTER,
 512        TokenType.FINAL,
 513        TokenType.FORMAT,
 514        TokenType.FULL,
 515        TokenType.GET,
 516        TokenType.IDENTIFIER,
 517        TokenType.IS,
 518        TokenType.ISNULL,
 519        TokenType.INTERVAL,
 520        TokenType.KEEP,
 521        TokenType.KILL,
 522        TokenType.LEFT,
 523        TokenType.LIMIT,
 524        TokenType.LOAD,
 525        TokenType.MERGE,
 526        TokenType.NATURAL,
 527        TokenType.NEXT,
 528        TokenType.OFFSET,
 529        TokenType.OPERATOR,
 530        TokenType.ORDINALITY,
 531        TokenType.OVERLAPS,
 532        TokenType.OVERWRITE,
 533        TokenType.PARTITION,
 534        TokenType.PERCENT,
 535        TokenType.PIVOT,
 536        TokenType.PRAGMA,
 537        TokenType.PUT,
 538        TokenType.RANGE,
 539        TokenType.RECURSIVE,
 540        TokenType.REFERENCES,
 541        TokenType.REFRESH,
 542        TokenType.RENAME,
 543        TokenType.REPLACE,
 544        TokenType.RIGHT,
 545        TokenType.ROLLUP,
 546        TokenType.ROW,
 547        TokenType.ROWS,
 548        TokenType.SEMI,
 549        TokenType.SET,
 550        TokenType.SETTINGS,
 551        TokenType.SHOW,
 552        TokenType.TEMPORARY,
 553        TokenType.TOP,
 554        TokenType.TRUE,
 555        TokenType.TRUNCATE,
 556        TokenType.UNIQUE,
 557        TokenType.UNNEST,
 558        TokenType.UNPIVOT,
 559        TokenType.UPDATE,
 560        TokenType.USE,
 561        TokenType.VOLATILE,
 562        TokenType.WINDOW,
 563        *CREATABLES,
 564        *SUBQUERY_PREDICATES,
 565        *TYPE_TOKENS,
 566        *NO_PAREN_FUNCTIONS,
 567    }
 568    ID_VAR_TOKENS.remove(TokenType.UNION)
 569
 570    TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
 571        TokenType.ANTI,
 572        TokenType.APPLY,
 573        TokenType.ASOF,
 574        TokenType.FULL,
 575        TokenType.LEFT,
 576        TokenType.LOCK,
 577        TokenType.NATURAL,
 578        TokenType.RIGHT,
 579        TokenType.SEMI,
 580        TokenType.WINDOW,
 581    }
 582
 583    ALIAS_TOKENS = ID_VAR_TOKENS
 584
 585    COLON_PLACEHOLDER_TOKENS = ID_VAR_TOKENS
 586
 587    ARRAY_CONSTRUCTORS = {
 588        "ARRAY": exp.Array,
 589        "LIST": exp.List,
 590    }
 591
 592    COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS}
 593
 594    UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET}
 595
 596    TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"}
 597
 598    FUNC_TOKENS = {
 599        TokenType.COLLATE,
 600        TokenType.COMMAND,
 601        TokenType.CURRENT_DATE,
 602        TokenType.CURRENT_DATETIME,
 603        TokenType.CURRENT_SCHEMA,
 604        TokenType.CURRENT_TIMESTAMP,
 605        TokenType.CURRENT_TIME,
 606        TokenType.CURRENT_USER,
 607        TokenType.FILTER,
 608        TokenType.FIRST,
 609        TokenType.FORMAT,
 610        TokenType.GET,
 611        TokenType.GLOB,
 612        TokenType.IDENTIFIER,
 613        TokenType.INDEX,
 614        TokenType.ISNULL,
 615        TokenType.ILIKE,
 616        TokenType.INSERT,
 617        TokenType.LIKE,
 618        TokenType.MERGE,
 619        TokenType.NEXT,
 620        TokenType.OFFSET,
 621        TokenType.PRIMARY_KEY,
 622        TokenType.RANGE,
 623        TokenType.REPLACE,
 624        TokenType.RLIKE,
 625        TokenType.ROW,
 626        TokenType.UNNEST,
 627        TokenType.VAR,
 628        TokenType.LEFT,
 629        TokenType.RIGHT,
 630        TokenType.SEQUENCE,
 631        TokenType.DATE,
 632        TokenType.DATETIME,
 633        TokenType.TABLE,
 634        TokenType.TIMESTAMP,
 635        TokenType.TIMESTAMPTZ,
 636        TokenType.TRUNCATE,
 637        TokenType.WINDOW,
 638        TokenType.XOR,
 639        *TYPE_TOKENS,
 640        *SUBQUERY_PREDICATES,
 641    }
 642
 643    CONJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 644        TokenType.AND: exp.And,
 645    }
 646
 647    ASSIGNMENT: t.Dict[TokenType, t.Type[exp.Expression]] = {
 648        TokenType.COLON_EQ: exp.PropertyEQ,
 649    }
 650
 651    DISJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 652        TokenType.OR: exp.Or,
 653    }
 654
 655    EQUALITY = {
 656        TokenType.EQ: exp.EQ,
 657        TokenType.NEQ: exp.NEQ,
 658        TokenType.NULLSAFE_EQ: exp.NullSafeEQ,
 659    }
 660
 661    COMPARISON = {
 662        TokenType.GT: exp.GT,
 663        TokenType.GTE: exp.GTE,
 664        TokenType.LT: exp.LT,
 665        TokenType.LTE: exp.LTE,
 666    }
 667
 668    BITWISE = {
 669        TokenType.AMP: exp.BitwiseAnd,
 670        TokenType.CARET: exp.BitwiseXor,
 671        TokenType.PIPE: exp.BitwiseOr,
 672    }
 673
 674    TERM = {
 675        TokenType.DASH: exp.Sub,
 676        TokenType.PLUS: exp.Add,
 677        TokenType.MOD: exp.Mod,
 678        TokenType.COLLATE: exp.Collate,
 679    }
 680
 681    FACTOR = {
 682        TokenType.DIV: exp.IntDiv,
 683        TokenType.LR_ARROW: exp.Distance,
 684        TokenType.SLASH: exp.Div,
 685        TokenType.STAR: exp.Mul,
 686    }
 687
 688    EXPONENT: t.Dict[TokenType, t.Type[exp.Expression]] = {}
 689
 690    TIMES = {
 691        TokenType.TIME,
 692        TokenType.TIMETZ,
 693    }
 694
 695    TIMESTAMPS = {
 696        TokenType.TIMESTAMP,
 697        TokenType.TIMESTAMPNTZ,
 698        TokenType.TIMESTAMPTZ,
 699        TokenType.TIMESTAMPLTZ,
 700        *TIMES,
 701    }
 702
 703    SET_OPERATIONS = {
 704        TokenType.UNION,
 705        TokenType.INTERSECT,
 706        TokenType.EXCEPT,
 707    }
 708
 709    JOIN_METHODS = {
 710        TokenType.ASOF,
 711        TokenType.NATURAL,
 712        TokenType.POSITIONAL,
 713    }
 714
 715    JOIN_SIDES = {
 716        TokenType.LEFT,
 717        TokenType.RIGHT,
 718        TokenType.FULL,
 719    }
 720
 721    JOIN_KINDS = {
 722        TokenType.ANTI,
 723        TokenType.CROSS,
 724        TokenType.INNER,
 725        TokenType.OUTER,
 726        TokenType.SEMI,
 727        TokenType.STRAIGHT_JOIN,
 728    }
 729
 730    JOIN_HINTS: t.Set[str] = set()
 731
 732    LAMBDAS = {
 733        TokenType.ARROW: lambda self, expressions: self.expression(
 734            exp.Lambda,
 735            this=self._replace_lambda(
 736                self._parse_assignment(),
 737                expressions,
 738            ),
 739            expressions=expressions,
 740        ),
 741        TokenType.FARROW: lambda self, expressions: self.expression(
 742            exp.Kwarg,
 743            this=exp.var(expressions[0].name),
 744            expression=self._parse_assignment(),
 745        ),
 746    }
 747
 748    COLUMN_OPERATORS = {
 749        TokenType.DOT: None,
 750        TokenType.DOTCOLON: lambda self, this, to: self.expression(
 751            exp.JSONCast,
 752            this=this,
 753            to=to,
 754        ),
 755        TokenType.DCOLON: lambda self, this, to: self.expression(
 756            exp.Cast if self.STRICT_CAST else exp.TryCast,
 757            this=this,
 758            to=to,
 759        ),
 760        TokenType.ARROW: lambda self, this, path: self.expression(
 761            exp.JSONExtract,
 762            this=this,
 763            expression=self.dialect.to_json_path(path),
 764            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 765        ),
 766        TokenType.DARROW: lambda self, this, path: self.expression(
 767            exp.JSONExtractScalar,
 768            this=this,
 769            expression=self.dialect.to_json_path(path),
 770            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 771        ),
 772        TokenType.HASH_ARROW: lambda self, this, path: self.expression(
 773            exp.JSONBExtract,
 774            this=this,
 775            expression=path,
 776        ),
 777        TokenType.DHASH_ARROW: lambda self, this, path: self.expression(
 778            exp.JSONBExtractScalar,
 779            this=this,
 780            expression=path,
 781        ),
 782        TokenType.PLACEHOLDER: lambda self, this, key: self.expression(
 783            exp.JSONBContains,
 784            this=this,
 785            expression=key,
 786        ),
 787    }
 788
 789    EXPRESSION_PARSERS = {
 790        exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
 791        exp.Column: lambda self: self._parse_column(),
 792        exp.Condition: lambda self: self._parse_assignment(),
 793        exp.DataType: lambda self: self._parse_types(allow_identifiers=False, schema=True),
 794        exp.Expression: lambda self: self._parse_expression(),
 795        exp.From: lambda self: self._parse_from(joins=True),
 796        exp.Group: lambda self: self._parse_group(),
 797        exp.Having: lambda self: self._parse_having(),
 798        exp.Hint: lambda self: self._parse_hint_body(),
 799        exp.Identifier: lambda self: self._parse_id_var(),
 800        exp.Join: lambda self: self._parse_join(),
 801        exp.Lambda: lambda self: self._parse_lambda(),
 802        exp.Lateral: lambda self: self._parse_lateral(),
 803        exp.Limit: lambda self: self._parse_limit(),
 804        exp.Offset: lambda self: self._parse_offset(),
 805        exp.Order: lambda self: self._parse_order(),
 806        exp.Ordered: lambda self: self._parse_ordered(),
 807        exp.Properties: lambda self: self._parse_properties(),
 808        exp.PartitionedByProperty: lambda self: self._parse_partitioned_by(),
 809        exp.Qualify: lambda self: self._parse_qualify(),
 810        exp.Returning: lambda self: self._parse_returning(),
 811        exp.Select: lambda self: self._parse_select(),
 812        exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY),
 813        exp.Table: lambda self: self._parse_table_parts(),
 814        exp.TableAlias: lambda self: self._parse_table_alias(),
 815        exp.Tuple: lambda self: self._parse_value(values=False),
 816        exp.Whens: lambda self: self._parse_when_matched(),
 817        exp.Where: lambda self: self._parse_where(),
 818        exp.Window: lambda self: self._parse_named_window(),
 819        exp.With: lambda self: self._parse_with(),
 820        "JOIN_TYPE": lambda self: self._parse_join_parts(),
 821    }
 822
 823    STATEMENT_PARSERS = {
 824        TokenType.ALTER: lambda self: self._parse_alter(),
 825        TokenType.ANALYZE: lambda self: self._parse_analyze(),
 826        TokenType.BEGIN: lambda self: self._parse_transaction(),
 827        TokenType.CACHE: lambda self: self._parse_cache(),
 828        TokenType.COMMENT: lambda self: self._parse_comment(),
 829        TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(),
 830        TokenType.COPY: lambda self: self._parse_copy(),
 831        TokenType.CREATE: lambda self: self._parse_create(),
 832        TokenType.DELETE: lambda self: self._parse_delete(),
 833        TokenType.DESC: lambda self: self._parse_describe(),
 834        TokenType.DESCRIBE: lambda self: self._parse_describe(),
 835        TokenType.DROP: lambda self: self._parse_drop(),
 836        TokenType.GRANT: lambda self: self._parse_grant(),
 837        TokenType.INSERT: lambda self: self._parse_insert(),
 838        TokenType.KILL: lambda self: self._parse_kill(),
 839        TokenType.LOAD: lambda self: self._parse_load(),
 840        TokenType.MERGE: lambda self: self._parse_merge(),
 841        TokenType.PIVOT: lambda self: self._parse_simplified_pivot(),
 842        TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()),
 843        TokenType.REFRESH: lambda self: self._parse_refresh(),
 844        TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(),
 845        TokenType.SET: lambda self: self._parse_set(),
 846        TokenType.TRUNCATE: lambda self: self._parse_truncate_table(),
 847        TokenType.UNCACHE: lambda self: self._parse_uncache(),
 848        TokenType.UNPIVOT: lambda self: self._parse_simplified_pivot(is_unpivot=True),
 849        TokenType.UPDATE: lambda self: self._parse_update(),
 850        TokenType.USE: lambda self: self._parse_use(),
 851        TokenType.SEMICOLON: lambda self: exp.Semicolon(),
 852    }
 853
 854    UNARY_PARSERS = {
 855        TokenType.PLUS: lambda self: self._parse_unary(),  # Unary + is handled as a no-op
 856        TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()),
 857        TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()),
 858        TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()),
 859        TokenType.PIPE_SLASH: lambda self: self.expression(exp.Sqrt, this=self._parse_unary()),
 860        TokenType.DPIPE_SLASH: lambda self: self.expression(exp.Cbrt, this=self._parse_unary()),
 861    }
 862
 863    STRING_PARSERS = {
 864        TokenType.HEREDOC_STRING: lambda self, token: self.expression(
 865            exp.RawString, this=token.text
 866        ),
 867        TokenType.NATIONAL_STRING: lambda self, token: self.expression(
 868            exp.National, this=token.text
 869        ),
 870        TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text),
 871        TokenType.STRING: lambda self, token: self.expression(
 872            exp.Literal, this=token.text, is_string=True
 873        ),
 874        TokenType.UNICODE_STRING: lambda self, token: self.expression(
 875            exp.UnicodeString,
 876            this=token.text,
 877            escape=self._match_text_seq("UESCAPE") and self._parse_string(),
 878        ),
 879    }
 880
 881    NUMERIC_PARSERS = {
 882        TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text),
 883        TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text),
 884        TokenType.HEX_STRING: lambda self, token: self.expression(
 885            exp.HexString,
 886            this=token.text,
 887            is_integer=self.dialect.HEX_STRING_IS_INTEGER_TYPE or None,
 888        ),
 889        TokenType.NUMBER: lambda self, token: self.expression(
 890            exp.Literal, this=token.text, is_string=False
 891        ),
 892    }
 893
 894    PRIMARY_PARSERS = {
 895        **STRING_PARSERS,
 896        **NUMERIC_PARSERS,
 897        TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token),
 898        TokenType.NULL: lambda self, _: self.expression(exp.Null),
 899        TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True),
 900        TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False),
 901        TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(),
 902        TokenType.STAR: lambda self, _: self._parse_star_ops(),
 903    }
 904
 905    PLACEHOLDER_PARSERS = {
 906        TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
 907        TokenType.PARAMETER: lambda self: self._parse_parameter(),
 908        TokenType.COLON: lambda self: (
 909            self.expression(exp.Placeholder, this=self._prev.text)
 910            if self._match_set(self.COLON_PLACEHOLDER_TOKENS)
 911            else None
 912        ),
 913    }
 914
 915    RANGE_PARSERS = {
 916        TokenType.AT_GT: binary_range_parser(exp.ArrayContainsAll),
 917        TokenType.BETWEEN: lambda self, this: self._parse_between(this),
 918        TokenType.GLOB: binary_range_parser(exp.Glob),
 919        TokenType.ILIKE: binary_range_parser(exp.ILike),
 920        TokenType.IN: lambda self, this: self._parse_in(this),
 921        TokenType.IRLIKE: binary_range_parser(exp.RegexpILike),
 922        TokenType.IS: lambda self, this: self._parse_is(this),
 923        TokenType.LIKE: binary_range_parser(exp.Like),
 924        TokenType.LT_AT: binary_range_parser(exp.ArrayContainsAll, reverse_args=True),
 925        TokenType.OVERLAPS: binary_range_parser(exp.Overlaps),
 926        TokenType.RLIKE: binary_range_parser(exp.RegexpLike),
 927        TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo),
 928        TokenType.FOR: lambda self, this: self._parse_comprehension(this),
 929    }
 930
 931    PROPERTY_PARSERS: t.Dict[str, t.Callable] = {
 932        "ALLOWED_VALUES": lambda self: self.expression(
 933            exp.AllowedValuesProperty, expressions=self._parse_csv(self._parse_primary)
 934        ),
 935        "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty),
 936        "AUTO": lambda self: self._parse_auto_property(),
 937        "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty),
 938        "BACKUP": lambda self: self.expression(
 939            exp.BackupProperty, this=self._parse_var(any_token=True)
 940        ),
 941        "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(),
 942        "CHARSET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 943        "CHARACTER SET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 944        "CHECKSUM": lambda self: self._parse_checksum(),
 945        "CLUSTER BY": lambda self: self._parse_cluster(),
 946        "CLUSTERED": lambda self: self._parse_clustered_by(),
 947        "COLLATE": lambda self, **kwargs: self._parse_property_assignment(
 948            exp.CollateProperty, **kwargs
 949        ),
 950        "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty),
 951        "CONTAINS": lambda self: self._parse_contains_property(),
 952        "COPY": lambda self: self._parse_copy_property(),
 953        "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs),
 954        "DATA_DELETION": lambda self: self._parse_data_deletion_property(),
 955        "DEFINER": lambda self: self._parse_definer(),
 956        "DETERMINISTIC": lambda self: self.expression(
 957            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 958        ),
 959        "DISTRIBUTED": lambda self: self._parse_distributed_property(),
 960        "DUPLICATE": lambda self: self._parse_composite_key_property(exp.DuplicateKeyProperty),
 961        "DYNAMIC": lambda self: self.expression(exp.DynamicProperty),
 962        "DISTKEY": lambda self: self._parse_distkey(),
 963        "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty),
 964        "EMPTY": lambda self: self.expression(exp.EmptyProperty),
 965        "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty),
 966        "ENVIRONMENT": lambda self: self.expression(
 967            exp.EnviromentProperty, expressions=self._parse_wrapped_csv(self._parse_assignment)
 968        ),
 969        "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty),
 970        "EXTERNAL": lambda self: self.expression(exp.ExternalProperty),
 971        "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs),
 972        "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
 973        "FREESPACE": lambda self: self._parse_freespace(),
 974        "GLOBAL": lambda self: self.expression(exp.GlobalProperty),
 975        "HEAP": lambda self: self.expression(exp.HeapProperty),
 976        "ICEBERG": lambda self: self.expression(exp.IcebergProperty),
 977        "IMMUTABLE": lambda self: self.expression(
 978            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 979        ),
 980        "INHERITS": lambda self: self.expression(
 981            exp.InheritsProperty, expressions=self._parse_wrapped_csv(self._parse_table)
 982        ),
 983        "INPUT": lambda self: self.expression(exp.InputModelProperty, this=self._parse_schema()),
 984        "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs),
 985        "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty),
 986        "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"),
 987        "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"),
 988        "LIKE": lambda self: self._parse_create_like(),
 989        "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty),
 990        "LOCK": lambda self: self._parse_locking(),
 991        "LOCKING": lambda self: self._parse_locking(),
 992        "LOG": lambda self, **kwargs: self._parse_log(**kwargs),
 993        "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty),
 994        "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs),
 995        "MODIFIES": lambda self: self._parse_modifies_property(),
 996        "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True),
 997        "NO": lambda self: self._parse_no_property(),
 998        "ON": lambda self: self._parse_on_property(),
 999        "ORDER BY": lambda self: self._parse_order(skip_order_token=True),
1000        "OUTPUT": lambda self: self.expression(exp.OutputModelProperty, this=self._parse_schema()),
1001        "PARTITION": lambda self: self._parse_partitioned_of(),
1002        "PARTITION BY": lambda self: self._parse_partitioned_by(),
1003        "PARTITIONED BY": lambda self: self._parse_partitioned_by(),
1004        "PARTITIONED_BY": lambda self: self._parse_partitioned_by(),
1005        "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True),
1006        "RANGE": lambda self: self._parse_dict_range(this="RANGE"),
1007        "READS": lambda self: self._parse_reads_property(),
1008        "REMOTE": lambda self: self._parse_remote_with_connection(),
1009        "RETURNS": lambda self: self._parse_returns(),
1010        "STRICT": lambda self: self.expression(exp.StrictProperty),
1011        "STREAMING": lambda self: self.expression(exp.StreamingTableProperty),
1012        "ROW": lambda self: self._parse_row(),
1013        "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty),
1014        "SAMPLE": lambda self: self.expression(
1015            exp.SampleProperty, this=self._match_text_seq("BY") and self._parse_bitwise()
1016        ),
1017        "SECURE": lambda self: self.expression(exp.SecureProperty),
1018        "SECURITY": lambda self: self._parse_security(),
1019        "SET": lambda self: self.expression(exp.SetProperty, multi=False),
1020        "SETTINGS": lambda self: self._parse_settings_property(),
1021        "SHARING": lambda self: self._parse_property_assignment(exp.SharingProperty),
1022        "SORTKEY": lambda self: self._parse_sortkey(),
1023        "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"),
1024        "STABLE": lambda self: self.expression(
1025            exp.StabilityProperty, this=exp.Literal.string("STABLE")
1026        ),
1027        "STORED": lambda self: self._parse_stored(),
1028        "SYSTEM_VERSIONING": lambda self: self._parse_system_versioning_property(),
1029        "TBLPROPERTIES": lambda self: self._parse_wrapped_properties(),
1030        "TEMP": lambda self: self.expression(exp.TemporaryProperty),
1031        "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty),
1032        "TO": lambda self: self._parse_to_table(),
1033        "TRANSIENT": lambda self: self.expression(exp.TransientProperty),
1034        "TRANSFORM": lambda self: self.expression(
1035            exp.TransformModelProperty, expressions=self._parse_wrapped_csv(self._parse_expression)
1036        ),
1037        "TTL": lambda self: self._parse_ttl(),
1038        "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
1039        "UNLOGGED": lambda self: self.expression(exp.UnloggedProperty),
1040        "VOLATILE": lambda self: self._parse_volatile_property(),
1041        "WITH": lambda self: self._parse_with_property(),
1042    }
1043
1044    CONSTRAINT_PARSERS = {
1045        "AUTOINCREMENT": lambda self: self._parse_auto_increment(),
1046        "AUTO_INCREMENT": lambda self: self._parse_auto_increment(),
1047        "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False),
1048        "CHARACTER SET": lambda self: self.expression(
1049            exp.CharacterSetColumnConstraint, this=self._parse_var_or_string()
1050        ),
1051        "CHECK": lambda self: self.expression(
1052            exp.CheckColumnConstraint,
1053            this=self._parse_wrapped(self._parse_assignment),
1054            enforced=self._match_text_seq("ENFORCED"),
1055        ),
1056        "COLLATE": lambda self: self.expression(
1057            exp.CollateColumnConstraint,
1058            this=self._parse_identifier() or self._parse_column(),
1059        ),
1060        "COMMENT": lambda self: self.expression(
1061            exp.CommentColumnConstraint, this=self._parse_string()
1062        ),
1063        "COMPRESS": lambda self: self._parse_compress(),
1064        "CLUSTERED": lambda self: self.expression(
1065            exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1066        ),
1067        "NONCLUSTERED": lambda self: self.expression(
1068            exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1069        ),
1070        "DEFAULT": lambda self: self.expression(
1071            exp.DefaultColumnConstraint, this=self._parse_bitwise()
1072        ),
1073        "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()),
1074        "EPHEMERAL": lambda self: self.expression(
1075            exp.EphemeralColumnConstraint, this=self._parse_bitwise()
1076        ),
1077        "EXCLUDE": lambda self: self.expression(
1078            exp.ExcludeColumnConstraint, this=self._parse_index_params()
1079        ),
1080        "FOREIGN KEY": lambda self: self._parse_foreign_key(),
1081        "FORMAT": lambda self: self.expression(
1082            exp.DateFormatColumnConstraint, this=self._parse_var_or_string()
1083        ),
1084        "GENERATED": lambda self: self._parse_generated_as_identity(),
1085        "IDENTITY": lambda self: self._parse_auto_increment(),
1086        "INLINE": lambda self: self._parse_inline(),
1087        "LIKE": lambda self: self._parse_create_like(),
1088        "NOT": lambda self: self._parse_not_constraint(),
1089        "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True),
1090        "ON": lambda self: (
1091            self._match(TokenType.UPDATE)
1092            and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function())
1093        )
1094        or self.expression(exp.OnProperty, this=self._parse_id_var()),
1095        "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()),
1096        "PERIOD": lambda self: self._parse_period_for_system_time(),
1097        "PRIMARY KEY": lambda self: self._parse_primary_key(),
1098        "REFERENCES": lambda self: self._parse_references(match=False),
1099        "TITLE": lambda self: self.expression(
1100            exp.TitleColumnConstraint, this=self._parse_var_or_string()
1101        ),
1102        "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]),
1103        "UNIQUE": lambda self: self._parse_unique(),
1104        "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint),
1105        "WATERMARK": lambda self: self.expression(
1106            exp.WatermarkColumnConstraint,
1107            this=self._match(TokenType.FOR) and self._parse_column(),
1108            expression=self._match(TokenType.ALIAS) and self._parse_disjunction(),
1109        ),
1110        "WITH": lambda self: self.expression(
1111            exp.Properties, expressions=self._parse_wrapped_properties()
1112        ),
1113        "BUCKET": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1114        "TRUNCATE": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1115    }
1116
1117    def _parse_partitioned_by_bucket_or_truncate(self) -> exp.Expression:
1118        klass = (
1119            exp.PartitionedByBucket
1120            if self._prev.text.upper() == "BUCKET"
1121            else exp.PartitionByTruncate
1122        )
1123
1124        args = self._parse_wrapped_csv(lambda: self._parse_primary() or self._parse_column())
1125        this, expression = seq_get(args, 0), seq_get(args, 1)
1126
1127        if isinstance(this, exp.Literal):
1128            # Check for Iceberg partition transforms (bucket / truncate) and ensure their arguments are in the right order
1129            #  - For Hive, it's `bucket(<num buckets>, <col name>)` or `truncate(<num_chars>, <col_name>)`
1130            #  - For Trino, it's reversed - `bucket(<col name>, <num buckets>)` or `truncate(<col_name>, <num_chars>)`
1131            # Both variants are canonicalized in the latter i.e `bucket(<col name>, <num buckets>)`
1132            #
1133            # Hive ref: https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-partitioning
1134            # Trino ref: https://docs.aws.amazon.com/athena/latest/ug/create-table-as.html#ctas-table-properties
1135            this, expression = expression, this
1136
1137        return self.expression(klass, this=this, expression=expression)
1138
1139    ALTER_PARSERS = {
1140        "ADD": lambda self: self._parse_alter_table_add(),
1141        "AS": lambda self: self._parse_select(),
1142        "ALTER": lambda self: self._parse_alter_table_alter(),
1143        "CLUSTER BY": lambda self: self._parse_cluster(wrapped=True),
1144        "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()),
1145        "DROP": lambda self: self._parse_alter_table_drop(),
1146        "RENAME": lambda self: self._parse_alter_table_rename(),
1147        "SET": lambda self: self._parse_alter_table_set(),
1148        "SWAP": lambda self: self.expression(
1149            exp.SwapTable, this=self._match(TokenType.WITH) and self._parse_table(schema=True)
1150        ),
1151    }
1152
1153    ALTER_ALTER_PARSERS = {
1154        "DISTKEY": lambda self: self._parse_alter_diststyle(),
1155        "DISTSTYLE": lambda self: self._parse_alter_diststyle(),
1156        "SORTKEY": lambda self: self._parse_alter_sortkey(),
1157        "COMPOUND": lambda self: self._parse_alter_sortkey(compound=True),
1158    }
1159
1160    SCHEMA_UNNAMED_CONSTRAINTS = {
1161        "CHECK",
1162        "EXCLUDE",
1163        "FOREIGN KEY",
1164        "LIKE",
1165        "PERIOD",
1166        "PRIMARY KEY",
1167        "UNIQUE",
1168        "WATERMARK",
1169        "BUCKET",
1170        "TRUNCATE",
1171    }
1172
1173    NO_PAREN_FUNCTION_PARSERS = {
1174        "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()),
1175        "CASE": lambda self: self._parse_case(),
1176        "CONNECT_BY_ROOT": lambda self: self.expression(
1177            exp.ConnectByRoot, this=self._parse_column()
1178        ),
1179        "IF": lambda self: self._parse_if(),
1180    }
1181
1182    INVALID_FUNC_NAME_TOKENS = {
1183        TokenType.IDENTIFIER,
1184        TokenType.STRING,
1185    }
1186
1187    FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
1188
1189    KEY_VALUE_DEFINITIONS = (exp.Alias, exp.EQ, exp.PropertyEQ, exp.Slice)
1190
1191    FUNCTION_PARSERS = {
1192        **{
1193            name: lambda self: self._parse_max_min_by(exp.ArgMax) for name in exp.ArgMax.sql_names()
1194        },
1195        **{
1196            name: lambda self: self._parse_max_min_by(exp.ArgMin) for name in exp.ArgMin.sql_names()
1197        },
1198        "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
1199        "CEIL": lambda self: self._parse_ceil_floor(exp.Ceil),
1200        "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
1201        "DECODE": lambda self: self._parse_decode(),
1202        "EXTRACT": lambda self: self._parse_extract(),
1203        "FLOOR": lambda self: self._parse_ceil_floor(exp.Floor),
1204        "GAP_FILL": lambda self: self._parse_gap_fill(),
1205        "JSON_OBJECT": lambda self: self._parse_json_object(),
1206        "JSON_OBJECTAGG": lambda self: self._parse_json_object(agg=True),
1207        "JSON_TABLE": lambda self: self._parse_json_table(),
1208        "MATCH": lambda self: self._parse_match_against(),
1209        "NORMALIZE": lambda self: self._parse_normalize(),
1210        "OPENJSON": lambda self: self._parse_open_json(),
1211        "OVERLAY": lambda self: self._parse_overlay(),
1212        "POSITION": lambda self: self._parse_position(),
1213        "PREDICT": lambda self: self._parse_predict(),
1214        "SAFE_CAST": lambda self: self._parse_cast(False, safe=True),
1215        "STRING_AGG": lambda self: self._parse_string_agg(),
1216        "SUBSTRING": lambda self: self._parse_substring(),
1217        "TRIM": lambda self: self._parse_trim(),
1218        "TRY_CAST": lambda self: self._parse_cast(False, safe=True),
1219        "TRY_CONVERT": lambda self: self._parse_convert(False, safe=True),
1220        "XMLELEMENT": lambda self: self.expression(
1221            exp.XMLElement,
1222            this=self._match_text_seq("NAME") and self._parse_id_var(),
1223            expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
1224        ),
1225        "XMLTABLE": lambda self: self._parse_xml_table(),
1226    }
1227
1228    QUERY_MODIFIER_PARSERS = {
1229        TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()),
1230        TokenType.PREWHERE: lambda self: ("prewhere", self._parse_prewhere()),
1231        TokenType.WHERE: lambda self: ("where", self._parse_where()),
1232        TokenType.GROUP_BY: lambda self: ("group", self._parse_group()),
1233        TokenType.HAVING: lambda self: ("having", self._parse_having()),
1234        TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()),
1235        TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()),
1236        TokenType.ORDER_BY: lambda self: ("order", self._parse_order()),
1237        TokenType.LIMIT: lambda self: ("limit", self._parse_limit()),
1238        TokenType.FETCH: lambda self: ("limit", self._parse_limit()),
1239        TokenType.OFFSET: lambda self: ("offset", self._parse_offset()),
1240        TokenType.FOR: lambda self: ("locks", self._parse_locks()),
1241        TokenType.LOCK: lambda self: ("locks", self._parse_locks()),
1242        TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1243        TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1244        TokenType.CLUSTER_BY: lambda self: (
1245            "cluster",
1246            self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
1247        ),
1248        TokenType.DISTRIBUTE_BY: lambda self: (
1249            "distribute",
1250            self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY),
1251        ),
1252        TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)),
1253        TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)),
1254        TokenType.START_WITH: lambda self: ("connect", self._parse_connect()),
1255    }
1256
1257    SET_PARSERS = {
1258        "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"),
1259        "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"),
1260        "SESSION": lambda self: self._parse_set_item_assignment("SESSION"),
1261        "TRANSACTION": lambda self: self._parse_set_transaction(),
1262    }
1263
1264    SHOW_PARSERS: t.Dict[str, t.Callable] = {}
1265
1266    TYPE_LITERAL_PARSERS = {
1267        exp.DataType.Type.JSON: lambda self, this, _: self.expression(exp.ParseJSON, this=this),
1268    }
1269
1270    TYPE_CONVERTERS: t.Dict[exp.DataType.Type, t.Callable[[exp.DataType], exp.DataType]] = {}
1271
1272    DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN}
1273
1274    PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE}
1275
1276    TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"}
1277    TRANSACTION_CHARACTERISTICS: OPTIONS_TYPE = {
1278        "ISOLATION": (
1279            ("LEVEL", "REPEATABLE", "READ"),
1280            ("LEVEL", "READ", "COMMITTED"),
1281            ("LEVEL", "READ", "UNCOMITTED"),
1282            ("LEVEL", "SERIALIZABLE"),
1283        ),
1284        "READ": ("WRITE", "ONLY"),
1285    }
1286
1287    CONFLICT_ACTIONS: OPTIONS_TYPE = dict.fromkeys(
1288        ("ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK", "UPDATE"), tuple()
1289    )
1290    CONFLICT_ACTIONS["DO"] = ("NOTHING", "UPDATE")
1291
1292    CREATE_SEQUENCE: OPTIONS_TYPE = {
1293        "SCALE": ("EXTEND", "NOEXTEND"),
1294        "SHARD": ("EXTEND", "NOEXTEND"),
1295        "NO": ("CYCLE", "CACHE", "MAXVALUE", "MINVALUE"),
1296        **dict.fromkeys(
1297            (
1298                "SESSION",
1299                "GLOBAL",
1300                "KEEP",
1301                "NOKEEP",
1302                "ORDER",
1303                "NOORDER",
1304                "NOCACHE",
1305                "CYCLE",
1306                "NOCYCLE",
1307                "NOMINVALUE",
1308                "NOMAXVALUE",
1309                "NOSCALE",
1310                "NOSHARD",
1311            ),
1312            tuple(),
1313        ),
1314    }
1315
1316    ISOLATED_LOADING_OPTIONS: OPTIONS_TYPE = {"FOR": ("ALL", "INSERT", "NONE")}
1317
1318    USABLES: OPTIONS_TYPE = dict.fromkeys(
1319        ("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA", "CATALOG"), tuple()
1320    )
1321
1322    CAST_ACTIONS: OPTIONS_TYPE = dict.fromkeys(("RENAME", "ADD"), ("FIELDS",))
1323
1324    SCHEMA_BINDING_OPTIONS: OPTIONS_TYPE = {
1325        "TYPE": ("EVOLUTION",),
1326        **dict.fromkeys(("BINDING", "COMPENSATION", "EVOLUTION"), tuple()),
1327    }
1328
1329    PROCEDURE_OPTIONS: OPTIONS_TYPE = {}
1330
1331    EXECUTE_AS_OPTIONS: OPTIONS_TYPE = dict.fromkeys(("CALLER", "SELF", "OWNER"), tuple())
1332
1333    KEY_CONSTRAINT_OPTIONS: OPTIONS_TYPE = {
1334        "NOT": ("ENFORCED",),
1335        "MATCH": (
1336            "FULL",
1337            "PARTIAL",
1338            "SIMPLE",
1339        ),
1340        "INITIALLY": ("DEFERRED", "IMMEDIATE"),
1341        "USING": (
1342            "BTREE",
1343            "HASH",
1344        ),
1345        **dict.fromkeys(("DEFERRABLE", "NORELY", "RELY"), tuple()),
1346    }
1347
1348    WINDOW_EXCLUDE_OPTIONS: OPTIONS_TYPE = {
1349        "NO": ("OTHERS",),
1350        "CURRENT": ("ROW",),
1351        **dict.fromkeys(("GROUP", "TIES"), tuple()),
1352    }
1353
1354    INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"}
1355
1356    CLONE_KEYWORDS = {"CLONE", "COPY"}
1357    HISTORICAL_DATA_PREFIX = {"AT", "BEFORE", "END"}
1358    HISTORICAL_DATA_KIND = {"TIMESTAMP", "OFFSET", "STATEMENT", "STREAM"}
1359
1360    OPCLASS_FOLLOW_KEYWORDS = {"ASC", "DESC", "NULLS", "WITH"}
1361
1362    OPTYPE_FOLLOW_TOKENS = {TokenType.COMMA, TokenType.R_PAREN}
1363
1364    TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE}
1365
1366    VIEW_ATTRIBUTES = {"ENCRYPTION", "SCHEMABINDING", "VIEW_METADATA"}
1367
1368    WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS}
1369    WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER}
1370    WINDOW_SIDES = {"FOLLOWING", "PRECEDING"}
1371
1372    JSON_KEY_VALUE_SEPARATOR_TOKENS = {TokenType.COLON, TokenType.COMMA, TokenType.IS}
1373
1374    FETCH_TOKENS = ID_VAR_TOKENS - {TokenType.ROW, TokenType.ROWS, TokenType.PERCENT}
1375
1376    ADD_CONSTRAINT_TOKENS = {
1377        TokenType.CONSTRAINT,
1378        TokenType.FOREIGN_KEY,
1379        TokenType.INDEX,
1380        TokenType.KEY,
1381        TokenType.PRIMARY_KEY,
1382        TokenType.UNIQUE,
1383    }
1384
1385    DISTINCT_TOKENS = {TokenType.DISTINCT}
1386
1387    NULL_TOKENS = {TokenType.NULL}
1388
1389    UNNEST_OFFSET_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - SET_OPERATIONS
1390
1391    SELECT_START_TOKENS = {TokenType.L_PAREN, TokenType.WITH, TokenType.SELECT}
1392
1393    COPY_INTO_VARLEN_OPTIONS = {"FILE_FORMAT", "COPY_OPTIONS", "FORMAT_OPTIONS", "CREDENTIAL"}
1394
1395    IS_JSON_PREDICATE_KIND = {"VALUE", "SCALAR", "ARRAY", "OBJECT"}
1396
1397    ODBC_DATETIME_LITERALS = {
1398        "d": exp.Date,
1399        "t": exp.Time,
1400        "ts": exp.Timestamp,
1401    }
1402
1403    ON_CONDITION_TOKENS = {"ERROR", "NULL", "TRUE", "FALSE", "EMPTY"}
1404
1405    PRIVILEGE_FOLLOW_TOKENS = {TokenType.ON, TokenType.COMMA, TokenType.L_PAREN}
1406
1407    # The style options for the DESCRIBE statement
1408    DESCRIBE_STYLES = {"ANALYZE", "EXTENDED", "FORMATTED", "HISTORY"}
1409
1410    # The style options for the ANALYZE statement
1411    ANALYZE_STYLES = {
1412        "BUFFER_USAGE_LIMIT",
1413        "FULL",
1414        "LOCAL",
1415        "NO_WRITE_TO_BINLOG",
1416        "SAMPLE",
1417        "SKIP_LOCKED",
1418        "VERBOSE",
1419    }
1420
1421    ANALYZE_EXPRESSION_PARSERS = {
1422        "ALL": lambda self: self._parse_analyze_columns(),
1423        "COMPUTE": lambda self: self._parse_analyze_statistics(),
1424        "DELETE": lambda self: self._parse_analyze_delete(),
1425        "DROP": lambda self: self._parse_analyze_histogram(),
1426        "ESTIMATE": lambda self: self._parse_analyze_statistics(),
1427        "LIST": lambda self: self._parse_analyze_list(),
1428        "PREDICATE": lambda self: self._parse_analyze_columns(),
1429        "UPDATE": lambda self: self._parse_analyze_histogram(),
1430        "VALIDATE": lambda self: self._parse_analyze_validate(),
1431    }
1432
1433    PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
1434
1435    AMBIGUOUS_ALIAS_TOKENS = (TokenType.LIMIT, TokenType.OFFSET)
1436
1437    OPERATION_MODIFIERS: t.Set[str] = set()
1438
1439    RECURSIVE_CTE_SEARCH_KIND = {"BREADTH", "DEPTH", "CYCLE"}
1440
1441    MODIFIABLES = (exp.Query, exp.Table, exp.TableFromRows)
1442
1443    STRICT_CAST = True
1444
1445    PREFIXED_PIVOT_COLUMNS = False
1446    IDENTIFY_PIVOT_STRINGS = False
1447
1448    LOG_DEFAULTS_TO_LN = False
1449
1450    # Whether ADD is present for each column added by ALTER TABLE
1451    ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN = True
1452
1453    # Whether the table sample clause expects CSV syntax
1454    TABLESAMPLE_CSV = False
1455
1456    # The default method used for table sampling
1457    DEFAULT_SAMPLING_METHOD: t.Optional[str] = None
1458
1459    # Whether the SET command needs a delimiter (e.g. "=") for assignments
1460    SET_REQUIRES_ASSIGNMENT_DELIMITER = True
1461
1462    # Whether the TRIM function expects the characters to trim as its first argument
1463    TRIM_PATTERN_FIRST = False
1464
1465    # Whether string aliases are supported `SELECT COUNT(*) 'count'`
1466    STRING_ALIASES = False
1467
1468    # Whether query modifiers such as LIMIT are attached to the UNION node (vs its right operand)
1469    MODIFIERS_ATTACHED_TO_SET_OP = True
1470    SET_OP_MODIFIERS = {"order", "limit", "offset"}
1471
1472    # Whether to parse IF statements that aren't followed by a left parenthesis as commands
1473    NO_PAREN_IF_COMMANDS = True
1474
1475    # Whether the -> and ->> operators expect documents of type JSON (e.g. Postgres)
1476    JSON_ARROWS_REQUIRE_JSON_TYPE = False
1477
1478    # Whether the `:` operator is used to extract a value from a VARIANT column
1479    COLON_IS_VARIANT_EXTRACT = False
1480
1481    # Whether or not a VALUES keyword needs to be followed by '(' to form a VALUES clause.
1482    # If this is True and '(' is not found, the keyword will be treated as an identifier
1483    VALUES_FOLLOWED_BY_PAREN = True
1484
1485    # Whether implicit unnesting is supported, e.g. SELECT 1 FROM y.z AS z, z.a (Redshift)
1486    SUPPORTS_IMPLICIT_UNNEST = False
1487
1488    # Whether or not interval spans are supported, INTERVAL 1 YEAR TO MONTHS
1489    INTERVAL_SPANS = True
1490
1491    # Whether a PARTITION clause can follow a table reference
1492    SUPPORTS_PARTITION_SELECTION = False
1493
1494    # Whether the `name AS expr` schema/column constraint requires parentheses around `expr`
1495    WRAPPED_TRANSFORM_COLUMN_CONSTRAINT = True
1496
1497    # Whether the 'AS' keyword is optional in the CTE definition syntax
1498    OPTIONAL_ALIAS_TOKEN_CTE = True
1499
1500    __slots__ = (
1501        "error_level",
1502        "error_message_context",
1503        "max_errors",
1504        "dialect",
1505        "sql",
1506        "errors",
1507        "_tokens",
1508        "_index",
1509        "_curr",
1510        "_next",
1511        "_prev",
1512        "_prev_comments",
1513    )
1514
1515    # Autofilled
1516    SHOW_TRIE: t.Dict = {}
1517    SET_TRIE: t.Dict = {}
1518
1519    def __init__(
1520        self,
1521        error_level: t.Optional[ErrorLevel] = None,
1522        error_message_context: int = 100,
1523        max_errors: int = 3,
1524        dialect: DialectType = None,
1525    ):
1526        from sqlglot.dialects import Dialect
1527
1528        self.error_level = error_level or ErrorLevel.IMMEDIATE
1529        self.error_message_context = error_message_context
1530        self.max_errors = max_errors
1531        self.dialect = Dialect.get_or_raise(dialect)
1532        self.reset()
1533
1534    def reset(self):
1535        self.sql = ""
1536        self.errors = []
1537        self._tokens = []
1538        self._index = 0
1539        self._curr = None
1540        self._next = None
1541        self._prev = None
1542        self._prev_comments = None
1543
1544    def parse(
1545        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1546    ) -> t.List[t.Optional[exp.Expression]]:
1547        """
1548        Parses a list of tokens and returns a list of syntax trees, one tree
1549        per parsed SQL statement.
1550
1551        Args:
1552            raw_tokens: The list of tokens.
1553            sql: The original SQL string, used to produce helpful debug messages.
1554
1555        Returns:
1556            The list of the produced syntax trees.
1557        """
1558        return self._parse(
1559            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1560        )
1561
1562    def parse_into(
1563        self,
1564        expression_types: exp.IntoType,
1565        raw_tokens: t.List[Token],
1566        sql: t.Optional[str] = None,
1567    ) -> t.List[t.Optional[exp.Expression]]:
1568        """
1569        Parses a list of tokens into a given Expression type. If a collection of Expression
1570        types is given instead, this method will try to parse the token list into each one
1571        of them, stopping at the first for which the parsing succeeds.
1572
1573        Args:
1574            expression_types: The expression type(s) to try and parse the token list into.
1575            raw_tokens: The list of tokens.
1576            sql: The original SQL string, used to produce helpful debug messages.
1577
1578        Returns:
1579            The target Expression.
1580        """
1581        errors = []
1582        for expression_type in ensure_list(expression_types):
1583            parser = self.EXPRESSION_PARSERS.get(expression_type)
1584            if not parser:
1585                raise TypeError(f"No parser registered for {expression_type}")
1586
1587            try:
1588                return self._parse(parser, raw_tokens, sql)
1589            except ParseError as e:
1590                e.errors[0]["into_expression"] = expression_type
1591                errors.append(e)
1592
1593        raise ParseError(
1594            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1595            errors=merge_errors(errors),
1596        ) from errors[-1]
1597
1598    def _parse(
1599        self,
1600        parse_method: t.Callable[[Parser], t.Optional[exp.Expression]],
1601        raw_tokens: t.List[Token],
1602        sql: t.Optional[str] = None,
1603    ) -> t.List[t.Optional[exp.Expression]]:
1604        self.reset()
1605        self.sql = sql or ""
1606
1607        total = len(raw_tokens)
1608        chunks: t.List[t.List[Token]] = [[]]
1609
1610        for i, token in enumerate(raw_tokens):
1611            if token.token_type == TokenType.SEMICOLON:
1612                if token.comments:
1613                    chunks.append([token])
1614
1615                if i < total - 1:
1616                    chunks.append([])
1617            else:
1618                chunks[-1].append(token)
1619
1620        expressions = []
1621
1622        for tokens in chunks:
1623            self._index = -1
1624            self._tokens = tokens
1625            self._advance()
1626
1627            expressions.append(parse_method(self))
1628
1629            if self._index < len(self._tokens):
1630                self.raise_error("Invalid expression / Unexpected token")
1631
1632            self.check_errors()
1633
1634        return expressions
1635
1636    def check_errors(self) -> None:
1637        """Logs or raises any found errors, depending on the chosen error level setting."""
1638        if self.error_level == ErrorLevel.WARN:
1639            for error in self.errors:
1640                logger.error(str(error))
1641        elif self.error_level == ErrorLevel.RAISE and self.errors:
1642            raise ParseError(
1643                concat_messages(self.errors, self.max_errors),
1644                errors=merge_errors(self.errors),
1645            )
1646
1647    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1648        """
1649        Appends an error in the list of recorded errors or raises it, depending on the chosen
1650        error level setting.
1651        """
1652        token = token or self._curr or self._prev or Token.string("")
1653        start = token.start
1654        end = token.end + 1
1655        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1656        highlight = self.sql[start:end]
1657        end_context = self.sql[end : end + self.error_message_context]
1658
1659        error = ParseError.new(
1660            f"{message}. Line {token.line}, Col: {token.col}.\n"
1661            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1662            description=message,
1663            line=token.line,
1664            col=token.col,
1665            start_context=start_context,
1666            highlight=highlight,
1667            end_context=end_context,
1668        )
1669
1670        if self.error_level == ErrorLevel.IMMEDIATE:
1671            raise error
1672
1673        self.errors.append(error)
1674
1675    def expression(
1676        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1677    ) -> E:
1678        """
1679        Creates a new, validated Expression.
1680
1681        Args:
1682            exp_class: The expression class to instantiate.
1683            comments: An optional list of comments to attach to the expression.
1684            kwargs: The arguments to set for the expression along with their respective values.
1685
1686        Returns:
1687            The target expression.
1688        """
1689        instance = exp_class(**kwargs)
1690        instance.add_comments(comments) if comments else self._add_comments(instance)
1691        return self.validate_expression(instance)
1692
1693    def _add_comments(self, expression: t.Optional[exp.Expression]) -> None:
1694        if expression and self._prev_comments:
1695            expression.add_comments(self._prev_comments)
1696            self._prev_comments = None
1697
1698    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1699        """
1700        Validates an Expression, making sure that all its mandatory arguments are set.
1701
1702        Args:
1703            expression: The expression to validate.
1704            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1705
1706        Returns:
1707            The validated expression.
1708        """
1709        if self.error_level != ErrorLevel.IGNORE:
1710            for error_message in expression.error_messages(args):
1711                self.raise_error(error_message)
1712
1713        return expression
1714
1715    def _find_sql(self, start: Token, end: Token) -> str:
1716        return self.sql[start.start : end.end + 1]
1717
1718    def _is_connected(self) -> bool:
1719        return self._prev and self._curr and self._prev.end + 1 == self._curr.start
1720
1721    def _advance(self, times: int = 1) -> None:
1722        self._index += times
1723        self._curr = seq_get(self._tokens, self._index)
1724        self._next = seq_get(self._tokens, self._index + 1)
1725
1726        if self._index > 0:
1727            self._prev = self._tokens[self._index - 1]
1728            self._prev_comments = self._prev.comments
1729        else:
1730            self._prev = None
1731            self._prev_comments = None
1732
1733    def _retreat(self, index: int) -> None:
1734        if index != self._index:
1735            self._advance(index - self._index)
1736
1737    def _warn_unsupported(self) -> None:
1738        if len(self._tokens) <= 1:
1739            return
1740
1741        # We use _find_sql because self.sql may comprise multiple chunks, and we're only
1742        # interested in emitting a warning for the one being currently processed.
1743        sql = self._find_sql(self._tokens[0], self._tokens[-1])[: self.error_message_context]
1744
1745        logger.warning(
1746            f"'{sql}' contains unsupported syntax. Falling back to parsing as a 'Command'."
1747        )
1748
1749    def _parse_command(self) -> exp.Command:
1750        self._warn_unsupported()
1751        return self.expression(
1752            exp.Command,
1753            comments=self._prev_comments,
1754            this=self._prev.text.upper(),
1755            expression=self._parse_string(),
1756        )
1757
1758    def _try_parse(self, parse_method: t.Callable[[], T], retreat: bool = False) -> t.Optional[T]:
1759        """
1760        Attemps to backtrack if a parse function that contains a try/catch internally raises an error.
1761        This behavior can be different depending on the uset-set ErrorLevel, so _try_parse aims to
1762        solve this by setting & resetting the parser state accordingly
1763        """
1764        index = self._index
1765        error_level = self.error_level
1766
1767        self.error_level = ErrorLevel.IMMEDIATE
1768        try:
1769            this = parse_method()
1770        except ParseError:
1771            this = None
1772        finally:
1773            if not this or retreat:
1774                self._retreat(index)
1775            self.error_level = error_level
1776
1777        return this
1778
1779    def _parse_comment(self, allow_exists: bool = True) -> exp.Expression:
1780        start = self._prev
1781        exists = self._parse_exists() if allow_exists else None
1782
1783        self._match(TokenType.ON)
1784
1785        materialized = self._match_text_seq("MATERIALIZED")
1786        kind = self._match_set(self.CREATABLES) and self._prev
1787        if not kind:
1788            return self._parse_as_command(start)
1789
1790        if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1791            this = self._parse_user_defined_function(kind=kind.token_type)
1792        elif kind.token_type == TokenType.TABLE:
1793            this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS)
1794        elif kind.token_type == TokenType.COLUMN:
1795            this = self._parse_column()
1796        else:
1797            this = self._parse_id_var()
1798
1799        self._match(TokenType.IS)
1800
1801        return self.expression(
1802            exp.Comment,
1803            this=this,
1804            kind=kind.text,
1805            expression=self._parse_string(),
1806            exists=exists,
1807            materialized=materialized,
1808        )
1809
1810    def _parse_to_table(
1811        self,
1812    ) -> exp.ToTableProperty:
1813        table = self._parse_table_parts(schema=True)
1814        return self.expression(exp.ToTableProperty, this=table)
1815
1816    # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl
1817    def _parse_ttl(self) -> exp.Expression:
1818        def _parse_ttl_action() -> t.Optional[exp.Expression]:
1819            this = self._parse_bitwise()
1820
1821            if self._match_text_seq("DELETE"):
1822                return self.expression(exp.MergeTreeTTLAction, this=this, delete=True)
1823            if self._match_text_seq("RECOMPRESS"):
1824                return self.expression(
1825                    exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise()
1826                )
1827            if self._match_text_seq("TO", "DISK"):
1828                return self.expression(
1829                    exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string()
1830                )
1831            if self._match_text_seq("TO", "VOLUME"):
1832                return self.expression(
1833                    exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string()
1834                )
1835
1836            return this
1837
1838        expressions = self._parse_csv(_parse_ttl_action)
1839        where = self._parse_where()
1840        group = self._parse_group()
1841
1842        aggregates = None
1843        if group and self._match(TokenType.SET):
1844            aggregates = self._parse_csv(self._parse_set_item)
1845
1846        return self.expression(
1847            exp.MergeTreeTTL,
1848            expressions=expressions,
1849            where=where,
1850            group=group,
1851            aggregates=aggregates,
1852        )
1853
1854    def _parse_statement(self) -> t.Optional[exp.Expression]:
1855        if self._curr is None:
1856            return None
1857
1858        if self._match_set(self.STATEMENT_PARSERS):
1859            comments = self._prev_comments
1860            stmt = self.STATEMENT_PARSERS[self._prev.token_type](self)
1861            stmt.add_comments(comments, prepend=True)
1862            return stmt
1863
1864        if self._match_set(self.dialect.tokenizer.COMMANDS):
1865            return self._parse_command()
1866
1867        expression = self._parse_expression()
1868        expression = self._parse_set_operations(expression) if expression else self._parse_select()
1869        return self._parse_query_modifiers(expression)
1870
1871    def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command:
1872        start = self._prev
1873        temporary = self._match(TokenType.TEMPORARY)
1874        materialized = self._match_text_seq("MATERIALIZED")
1875
1876        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
1877        if not kind:
1878            return self._parse_as_command(start)
1879
1880        concurrently = self._match_text_seq("CONCURRENTLY")
1881        if_exists = exists or self._parse_exists()
1882
1883        if kind == "COLUMN":
1884            this = self._parse_column()
1885        else:
1886            this = self._parse_table_parts(
1887                schema=True, is_db_reference=self._prev.token_type == TokenType.SCHEMA
1888            )
1889
1890        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
1891
1892        if self._match(TokenType.L_PAREN, advance=False):
1893            expressions = self._parse_wrapped_csv(self._parse_types)
1894        else:
1895            expressions = None
1896
1897        return self.expression(
1898            exp.Drop,
1899            exists=if_exists,
1900            this=this,
1901            expressions=expressions,
1902            kind=self.dialect.CREATABLE_KIND_MAPPING.get(kind) or kind,
1903            temporary=temporary,
1904            materialized=materialized,
1905            cascade=self._match_text_seq("CASCADE"),
1906            constraints=self._match_text_seq("CONSTRAINTS"),
1907            purge=self._match_text_seq("PURGE"),
1908            cluster=cluster,
1909            concurrently=concurrently,
1910        )
1911
1912    def _parse_exists(self, not_: bool = False) -> t.Optional[bool]:
1913        return (
1914            self._match_text_seq("IF")
1915            and (not not_ or self._match(TokenType.NOT))
1916            and self._match(TokenType.EXISTS)
1917        )
1918
1919    def _parse_create(self) -> exp.Create | exp.Command:
1920        # Note: this can't be None because we've matched a statement parser
1921        start = self._prev
1922
1923        replace = (
1924            start.token_type == TokenType.REPLACE
1925            or self._match_pair(TokenType.OR, TokenType.REPLACE)
1926            or self._match_pair(TokenType.OR, TokenType.ALTER)
1927        )
1928        refresh = self._match_pair(TokenType.OR, TokenType.REFRESH)
1929
1930        unique = self._match(TokenType.UNIQUE)
1931
1932        if self._match_text_seq("CLUSTERED", "COLUMNSTORE"):
1933            clustered = True
1934        elif self._match_text_seq("NONCLUSTERED", "COLUMNSTORE") or self._match_text_seq(
1935            "COLUMNSTORE"
1936        ):
1937            clustered = False
1938        else:
1939            clustered = None
1940
1941        if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False):
1942            self._advance()
1943
1944        properties = None
1945        create_token = self._match_set(self.CREATABLES) and self._prev
1946
1947        if not create_token:
1948            # exp.Properties.Location.POST_CREATE
1949            properties = self._parse_properties()
1950            create_token = self._match_set(self.CREATABLES) and self._prev
1951
1952            if not properties or not create_token:
1953                return self._parse_as_command(start)
1954
1955        concurrently = self._match_text_seq("CONCURRENTLY")
1956        exists = self._parse_exists(not_=True)
1957        this = None
1958        expression: t.Optional[exp.Expression] = None
1959        indexes = None
1960        no_schema_binding = None
1961        begin = None
1962        end = None
1963        clone = None
1964
1965        def extend_props(temp_props: t.Optional[exp.Properties]) -> None:
1966            nonlocal properties
1967            if properties and temp_props:
1968                properties.expressions.extend(temp_props.expressions)
1969            elif temp_props:
1970                properties = temp_props
1971
1972        if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1973            this = self._parse_user_defined_function(kind=create_token.token_type)
1974
1975            # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature)
1976            extend_props(self._parse_properties())
1977
1978            expression = self._match(TokenType.ALIAS) and self._parse_heredoc()
1979            extend_props(self._parse_properties())
1980
1981            if not expression:
1982                if self._match(TokenType.COMMAND):
1983                    expression = self._parse_as_command(self._prev)
1984                else:
1985                    begin = self._match(TokenType.BEGIN)
1986                    return_ = self._match_text_seq("RETURN")
1987
1988                    if self._match(TokenType.STRING, advance=False):
1989                        # Takes care of BigQuery's JavaScript UDF definitions that end in an OPTIONS property
1990                        # # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement
1991                        expression = self._parse_string()
1992                        extend_props(self._parse_properties())
1993                    else:
1994                        expression = self._parse_user_defined_function_expression()
1995
1996                    end = self._match_text_seq("END")
1997
1998                    if return_:
1999                        expression = self.expression(exp.Return, this=expression)
2000        elif create_token.token_type == TokenType.INDEX:
2001            # Postgres allows anonymous indexes, eg. CREATE INDEX IF NOT EXISTS ON t(c)
2002            if not self._match(TokenType.ON):
2003                index = self._parse_id_var()
2004                anonymous = False
2005            else:
2006                index = None
2007                anonymous = True
2008
2009            this = self._parse_index(index=index, anonymous=anonymous)
2010        elif create_token.token_type in self.DB_CREATABLES:
2011            table_parts = self._parse_table_parts(
2012                schema=True, is_db_reference=create_token.token_type == TokenType.SCHEMA
2013            )
2014
2015            # exp.Properties.Location.POST_NAME
2016            self._match(TokenType.COMMA)
2017            extend_props(self._parse_properties(before=True))
2018
2019            this = self._parse_schema(this=table_parts)
2020
2021            # exp.Properties.Location.POST_SCHEMA and POST_WITH
2022            extend_props(self._parse_properties())
2023
2024            has_alias = self._match(TokenType.ALIAS)
2025            if not self._match_set(self.DDL_SELECT_TOKENS, advance=False):
2026                # exp.Properties.Location.POST_ALIAS
2027                extend_props(self._parse_properties())
2028
2029            if create_token.token_type == TokenType.SEQUENCE:
2030                expression = self._parse_types()
2031                extend_props(self._parse_properties())
2032            else:
2033                expression = self._parse_ddl_select()
2034
2035                # Some dialects also support using a table as an alias instead of a SELECT.
2036                # Here we fallback to this as an alternative.
2037                if not expression and has_alias:
2038                    expression = self._try_parse(self._parse_table_parts)
2039
2040            if create_token.token_type == TokenType.TABLE:
2041                # exp.Properties.Location.POST_EXPRESSION
2042                extend_props(self._parse_properties())
2043
2044                indexes = []
2045                while True:
2046                    index = self._parse_index()
2047
2048                    # exp.Properties.Location.POST_INDEX
2049                    extend_props(self._parse_properties())
2050                    if not index:
2051                        break
2052                    else:
2053                        self._match(TokenType.COMMA)
2054                        indexes.append(index)
2055            elif create_token.token_type == TokenType.VIEW:
2056                if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"):
2057                    no_schema_binding = True
2058            elif create_token.token_type in (TokenType.SINK, TokenType.SOURCE):
2059                extend_props(self._parse_properties())
2060
2061            shallow = self._match_text_seq("SHALLOW")
2062
2063            if self._match_texts(self.CLONE_KEYWORDS):
2064                copy = self._prev.text.lower() == "copy"
2065                clone = self.expression(
2066                    exp.Clone, this=self._parse_table(schema=True), shallow=shallow, copy=copy
2067                )
2068
2069        if self._curr and not self._match_set((TokenType.R_PAREN, TokenType.COMMA), advance=False):
2070            return self._parse_as_command(start)
2071
2072        create_kind_text = create_token.text.upper()
2073        return self.expression(
2074            exp.Create,
2075            this=this,
2076            kind=self.dialect.CREATABLE_KIND_MAPPING.get(create_kind_text) or create_kind_text,
2077            replace=replace,
2078            refresh=refresh,
2079            unique=unique,
2080            expression=expression,
2081            exists=exists,
2082            properties=properties,
2083            indexes=indexes,
2084            no_schema_binding=no_schema_binding,
2085            begin=begin,
2086            end=end,
2087            clone=clone,
2088            concurrently=concurrently,
2089            clustered=clustered,
2090        )
2091
2092    def _parse_sequence_properties(self) -> t.Optional[exp.SequenceProperties]:
2093        seq = exp.SequenceProperties()
2094
2095        options = []
2096        index = self._index
2097
2098        while self._curr:
2099            self._match(TokenType.COMMA)
2100            if self._match_text_seq("INCREMENT"):
2101                self._match_text_seq("BY")
2102                self._match_text_seq("=")
2103                seq.set("increment", self._parse_term())
2104            elif self._match_text_seq("MINVALUE"):
2105                seq.set("minvalue", self._parse_term())
2106            elif self._match_text_seq("MAXVALUE"):
2107                seq.set("maxvalue", self._parse_term())
2108            elif self._match(TokenType.START_WITH) or self._match_text_seq("START"):
2109                self._match_text_seq("=")
2110                seq.set("start", self._parse_term())
2111            elif self._match_text_seq("CACHE"):
2112                # T-SQL allows empty CACHE which is initialized dynamically
2113                seq.set("cache", self._parse_number() or True)
2114            elif self._match_text_seq("OWNED", "BY"):
2115                # "OWNED BY NONE" is the default
2116                seq.set("owned", None if self._match_text_seq("NONE") else self._parse_column())
2117            else:
2118                opt = self._parse_var_from_options(self.CREATE_SEQUENCE, raise_unmatched=False)
2119                if opt:
2120                    options.append(opt)
2121                else:
2122                    break
2123
2124        seq.set("options", options if options else None)
2125        return None if self._index == index else seq
2126
2127    def _parse_property_before(self) -> t.Optional[exp.Expression]:
2128        # only used for teradata currently
2129        self._match(TokenType.COMMA)
2130
2131        kwargs = {
2132            "no": self._match_text_seq("NO"),
2133            "dual": self._match_text_seq("DUAL"),
2134            "before": self._match_text_seq("BEFORE"),
2135            "default": self._match_text_seq("DEFAULT"),
2136            "local": (self._match_text_seq("LOCAL") and "LOCAL")
2137            or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"),
2138            "after": self._match_text_seq("AFTER"),
2139            "minimum": self._match_texts(("MIN", "MINIMUM")),
2140            "maximum": self._match_texts(("MAX", "MAXIMUM")),
2141        }
2142
2143        if self._match_texts(self.PROPERTY_PARSERS):
2144            parser = self.PROPERTY_PARSERS[self._prev.text.upper()]
2145            try:
2146                return parser(self, **{k: v for k, v in kwargs.items() if v})
2147            except TypeError:
2148                self.raise_error(f"Cannot parse property '{self._prev.text}'")
2149
2150        return None
2151
2152    def _parse_wrapped_properties(self) -> t.List[exp.Expression]:
2153        return self._parse_wrapped_csv(self._parse_property)
2154
2155    def _parse_property(self) -> t.Optional[exp.Expression]:
2156        if self._match_texts(self.PROPERTY_PARSERS):
2157            return self.PROPERTY_PARSERS[self._prev.text.upper()](self)
2158
2159        if self._match(TokenType.DEFAULT) and self._match_texts(self.PROPERTY_PARSERS):
2160            return self.PROPERTY_PARSERS[self._prev.text.upper()](self, default=True)
2161
2162        if self._match_text_seq("COMPOUND", "SORTKEY"):
2163            return self._parse_sortkey(compound=True)
2164
2165        if self._match_text_seq("SQL", "SECURITY"):
2166            return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
2167
2168        index = self._index
2169        key = self._parse_column()
2170
2171        if not self._match(TokenType.EQ):
2172            self._retreat(index)
2173            return self._parse_sequence_properties()
2174
2175        # Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
2176        if isinstance(key, exp.Column):
2177            key = key.to_dot() if len(key.parts) > 1 else exp.var(key.name)
2178
2179        value = self._parse_bitwise() or self._parse_var(any_token=True)
2180
2181        # Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
2182        if isinstance(value, exp.Column):
2183            value = exp.var(value.name)
2184
2185        return self.expression(exp.Property, this=key, value=value)
2186
2187    def _parse_stored(self) -> t.Union[exp.FileFormatProperty, exp.StorageHandlerProperty]:
2188        if self._match_text_seq("BY"):
2189            return self.expression(exp.StorageHandlerProperty, this=self._parse_var_or_string())
2190
2191        self._match(TokenType.ALIAS)
2192        input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None
2193        output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None
2194
2195        return self.expression(
2196            exp.FileFormatProperty,
2197            this=(
2198                self.expression(
2199                    exp.InputOutputFormat,
2200                    input_format=input_format,
2201                    output_format=output_format,
2202                )
2203                if input_format or output_format
2204                else self._parse_var_or_string() or self._parse_number() or self._parse_id_var()
2205            ),
2206        )
2207
2208    def _parse_unquoted_field(self) -> t.Optional[exp.Expression]:
2209        field = self._parse_field()
2210        if isinstance(field, exp.Identifier) and not field.quoted:
2211            field = exp.var(field)
2212
2213        return field
2214
2215    def _parse_property_assignment(self, exp_class: t.Type[E], **kwargs: t.Any) -> E:
2216        self._match(TokenType.EQ)
2217        self._match(TokenType.ALIAS)
2218
2219        return self.expression(exp_class, this=self._parse_unquoted_field(), **kwargs)
2220
2221    def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]:
2222        properties = []
2223        while True:
2224            if before:
2225                prop = self._parse_property_before()
2226            else:
2227                prop = self._parse_property()
2228            if not prop:
2229                break
2230            for p in ensure_list(prop):
2231                properties.append(p)
2232
2233        if properties:
2234            return self.expression(exp.Properties, expressions=properties)
2235
2236        return None
2237
2238    def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty:
2239        return self.expression(
2240            exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION")
2241        )
2242
2243    def _parse_security(self) -> t.Optional[exp.SecurityProperty]:
2244        if self._match_texts(("DEFINER", "INVOKER")):
2245            security_specifier = self._prev.text.upper()
2246            return self.expression(exp.SecurityProperty, this=security_specifier)
2247        return None
2248
2249    def _parse_settings_property(self) -> exp.SettingsProperty:
2250        return self.expression(
2251            exp.SettingsProperty, expressions=self._parse_csv(self._parse_assignment)
2252        )
2253
2254    def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty:
2255        if self._index >= 2:
2256            pre_volatile_token = self._tokens[self._index - 2]
2257        else:
2258            pre_volatile_token = None
2259
2260        if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS:
2261            return exp.VolatileProperty()
2262
2263        return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE"))
2264
2265    def _parse_retention_period(self) -> exp.Var:
2266        # Parse TSQL's HISTORY_RETENTION_PERIOD: {INFINITE | <number> DAY | DAYS | MONTH ...}
2267        number = self._parse_number()
2268        number_str = f"{number} " if number else ""
2269        unit = self._parse_var(any_token=True)
2270        return exp.var(f"{number_str}{unit}")
2271
2272    def _parse_system_versioning_property(
2273        self, with_: bool = False
2274    ) -> exp.WithSystemVersioningProperty:
2275        self._match(TokenType.EQ)
2276        prop = self.expression(
2277            exp.WithSystemVersioningProperty,
2278            **{  # type: ignore
2279                "on": True,
2280                "with": with_,
2281            },
2282        )
2283
2284        if self._match_text_seq("OFF"):
2285            prop.set("on", False)
2286            return prop
2287
2288        self._match(TokenType.ON)
2289        if self._match(TokenType.L_PAREN):
2290            while self._curr and not self._match(TokenType.R_PAREN):
2291                if self._match_text_seq("HISTORY_TABLE", "="):
2292                    prop.set("this", self._parse_table_parts())
2293                elif self._match_text_seq("DATA_CONSISTENCY_CHECK", "="):
2294                    prop.set("data_consistency", self._advance_any() and self._prev.text.upper())
2295                elif self._match_text_seq("HISTORY_RETENTION_PERIOD", "="):
2296                    prop.set("retention_period", self._parse_retention_period())
2297
2298                self._match(TokenType.COMMA)
2299
2300        return prop
2301
2302    def _parse_data_deletion_property(self) -> exp.DataDeletionProperty:
2303        self._match(TokenType.EQ)
2304        on = self._match_text_seq("ON") or not self._match_text_seq("OFF")
2305        prop = self.expression(exp.DataDeletionProperty, on=on)
2306
2307        if self._match(TokenType.L_PAREN):
2308            while self._curr and not self._match(TokenType.R_PAREN):
2309                if self._match_text_seq("FILTER_COLUMN", "="):
2310                    prop.set("filter_column", self._parse_column())
2311                elif self._match_text_seq("RETENTION_PERIOD", "="):
2312                    prop.set("retention_period", self._parse_retention_period())
2313
2314                self._match(TokenType.COMMA)
2315
2316        return prop
2317
2318    def _parse_distributed_property(self) -> exp.DistributedByProperty:
2319        kind = "HASH"
2320        expressions: t.Optional[t.List[exp.Expression]] = None
2321        if self._match_text_seq("BY", "HASH"):
2322            expressions = self._parse_wrapped_csv(self._parse_id_var)
2323        elif self._match_text_seq("BY", "RANDOM"):
2324            kind = "RANDOM"
2325
2326        # If the BUCKETS keyword is not present, the number of buckets is AUTO
2327        buckets: t.Optional[exp.Expression] = None
2328        if self._match_text_seq("BUCKETS") and not self._match_text_seq("AUTO"):
2329            buckets = self._parse_number()
2330
2331        return self.expression(
2332            exp.DistributedByProperty,
2333            expressions=expressions,
2334            kind=kind,
2335            buckets=buckets,
2336            order=self._parse_order(),
2337        )
2338
2339    def _parse_composite_key_property(self, expr_type: t.Type[E]) -> E:
2340        self._match_text_seq("KEY")
2341        expressions = self._parse_wrapped_id_vars()
2342        return self.expression(expr_type, expressions=expressions)
2343
2344    def _parse_with_property(self) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
2345        if self._match_text_seq("(", "SYSTEM_VERSIONING"):
2346            prop = self._parse_system_versioning_property(with_=True)
2347            self._match_r_paren()
2348            return prop
2349
2350        if self._match(TokenType.L_PAREN, advance=False):
2351            return self._parse_wrapped_properties()
2352
2353        if self._match_text_seq("JOURNAL"):
2354            return self._parse_withjournaltable()
2355
2356        if self._match_texts(self.VIEW_ATTRIBUTES):
2357            return self.expression(exp.ViewAttributeProperty, this=self._prev.text.upper())
2358
2359        if self._match_text_seq("DATA"):
2360            return self._parse_withdata(no=False)
2361        elif self._match_text_seq("NO", "DATA"):
2362            return self._parse_withdata(no=True)
2363
2364        if self._match(TokenType.SERDE_PROPERTIES, advance=False):
2365            return self._parse_serde_properties(with_=True)
2366
2367        if self._match(TokenType.SCHEMA):
2368            return self.expression(
2369                exp.WithSchemaBindingProperty,
2370                this=self._parse_var_from_options(self.SCHEMA_BINDING_OPTIONS),
2371            )
2372
2373        if self._match_texts(self.PROCEDURE_OPTIONS, advance=False):
2374            return self.expression(
2375                exp.WithProcedureOptions, expressions=self._parse_csv(self._parse_procedure_option)
2376            )
2377
2378        if not self._next:
2379            return None
2380
2381        return self._parse_withisolatedloading()
2382
2383    def _parse_procedure_option(self) -> exp.Expression | None:
2384        if self._match_text_seq("EXECUTE", "AS"):
2385            return self.expression(
2386                exp.ExecuteAsProperty,
2387                this=self._parse_var_from_options(self.EXECUTE_AS_OPTIONS, raise_unmatched=False)
2388                or self._parse_string(),
2389            )
2390
2391        return self._parse_var_from_options(self.PROCEDURE_OPTIONS)
2392
2393    # https://dev.mysql.com/doc/refman/8.0/en/create-view.html
2394    def _parse_definer(self) -> t.Optional[exp.DefinerProperty]:
2395        self._match(TokenType.EQ)
2396
2397        user = self._parse_id_var()
2398        self._match(TokenType.PARAMETER)
2399        host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text)
2400
2401        if not user or not host:
2402            return None
2403
2404        return exp.DefinerProperty(this=f"{user}@{host}")
2405
2406    def _parse_withjournaltable(self) -> exp.WithJournalTableProperty:
2407        self._match(TokenType.TABLE)
2408        self._match(TokenType.EQ)
2409        return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts())
2410
2411    def _parse_log(self, no: bool = False) -> exp.LogProperty:
2412        return self.expression(exp.LogProperty, no=no)
2413
2414    def _parse_journal(self, **kwargs) -> exp.JournalProperty:
2415        return self.expression(exp.JournalProperty, **kwargs)
2416
2417    def _parse_checksum(self) -> exp.ChecksumProperty:
2418        self._match(TokenType.EQ)
2419
2420        on = None
2421        if self._match(TokenType.ON):
2422            on = True
2423        elif self._match_text_seq("OFF"):
2424            on = False
2425
2426        return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT))
2427
2428    def _parse_cluster(self, wrapped: bool = False) -> exp.Cluster:
2429        return self.expression(
2430            exp.Cluster,
2431            expressions=(
2432                self._parse_wrapped_csv(self._parse_ordered)
2433                if wrapped
2434                else self._parse_csv(self._parse_ordered)
2435            ),
2436        )
2437
2438    def _parse_clustered_by(self) -> exp.ClusteredByProperty:
2439        self._match_text_seq("BY")
2440
2441        self._match_l_paren()
2442        expressions = self._parse_csv(self._parse_column)
2443        self._match_r_paren()
2444
2445        if self._match_text_seq("SORTED", "BY"):
2446            self._match_l_paren()
2447            sorted_by = self._parse_csv(self._parse_ordered)
2448            self._match_r_paren()
2449        else:
2450            sorted_by = None
2451
2452        self._match(TokenType.INTO)
2453        buckets = self._parse_number()
2454        self._match_text_seq("BUCKETS")
2455
2456        return self.expression(
2457            exp.ClusteredByProperty,
2458            expressions=expressions,
2459            sorted_by=sorted_by,
2460            buckets=buckets,
2461        )
2462
2463    def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]:
2464        if not self._match_text_seq("GRANTS"):
2465            self._retreat(self._index - 1)
2466            return None
2467
2468        return self.expression(exp.CopyGrantsProperty)
2469
2470    def _parse_freespace(self) -> exp.FreespaceProperty:
2471        self._match(TokenType.EQ)
2472        return self.expression(
2473            exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT)
2474        )
2475
2476    def _parse_mergeblockratio(
2477        self, no: bool = False, default: bool = False
2478    ) -> exp.MergeBlockRatioProperty:
2479        if self._match(TokenType.EQ):
2480            return self.expression(
2481                exp.MergeBlockRatioProperty,
2482                this=self._parse_number(),
2483                percent=self._match(TokenType.PERCENT),
2484            )
2485
2486        return self.expression(exp.MergeBlockRatioProperty, no=no, default=default)
2487
2488    def _parse_datablocksize(
2489        self,
2490        default: t.Optional[bool] = None,
2491        minimum: t.Optional[bool] = None,
2492        maximum: t.Optional[bool] = None,
2493    ) -> exp.DataBlocksizeProperty:
2494        self._match(TokenType.EQ)
2495        size = self._parse_number()
2496
2497        units = None
2498        if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")):
2499            units = self._prev.text
2500
2501        return self.expression(
2502            exp.DataBlocksizeProperty,
2503            size=size,
2504            units=units,
2505            default=default,
2506            minimum=minimum,
2507            maximum=maximum,
2508        )
2509
2510    def _parse_blockcompression(self) -> exp.BlockCompressionProperty:
2511        self._match(TokenType.EQ)
2512        always = self._match_text_seq("ALWAYS")
2513        manual = self._match_text_seq("MANUAL")
2514        never = self._match_text_seq("NEVER")
2515        default = self._match_text_seq("DEFAULT")
2516
2517        autotemp = None
2518        if self._match_text_seq("AUTOTEMP"):
2519            autotemp = self._parse_schema()
2520
2521        return self.expression(
2522            exp.BlockCompressionProperty,
2523            always=always,
2524            manual=manual,
2525            never=never,
2526            default=default,
2527            autotemp=autotemp,
2528        )
2529
2530    def _parse_withisolatedloading(self) -> t.Optional[exp.IsolatedLoadingProperty]:
2531        index = self._index
2532        no = self._match_text_seq("NO")
2533        concurrent = self._match_text_seq("CONCURRENT")
2534
2535        if not self._match_text_seq("ISOLATED", "LOADING"):
2536            self._retreat(index)
2537            return None
2538
2539        target = self._parse_var_from_options(self.ISOLATED_LOADING_OPTIONS, raise_unmatched=False)
2540        return self.expression(
2541            exp.IsolatedLoadingProperty, no=no, concurrent=concurrent, target=target
2542        )
2543
2544    def _parse_locking(self) -> exp.LockingProperty:
2545        if self._match(TokenType.TABLE):
2546            kind = "TABLE"
2547        elif self._match(TokenType.VIEW):
2548            kind = "VIEW"
2549        elif self._match(TokenType.ROW):
2550            kind = "ROW"
2551        elif self._match_text_seq("DATABASE"):
2552            kind = "DATABASE"
2553        else:
2554            kind = None
2555
2556        if kind in ("DATABASE", "TABLE", "VIEW"):
2557            this = self._parse_table_parts()
2558        else:
2559            this = None
2560
2561        if self._match(TokenType.FOR):
2562            for_or_in = "FOR"
2563        elif self._match(TokenType.IN):
2564            for_or_in = "IN"
2565        else:
2566            for_or_in = None
2567
2568        if self._match_text_seq("ACCESS"):
2569            lock_type = "ACCESS"
2570        elif self._match_texts(("EXCL", "EXCLUSIVE")):
2571            lock_type = "EXCLUSIVE"
2572        elif self._match_text_seq("SHARE"):
2573            lock_type = "SHARE"
2574        elif self._match_text_seq("READ"):
2575            lock_type = "READ"
2576        elif self._match_text_seq("WRITE"):
2577            lock_type = "WRITE"
2578        elif self._match_text_seq("CHECKSUM"):
2579            lock_type = "CHECKSUM"
2580        else:
2581            lock_type = None
2582
2583        override = self._match_text_seq("OVERRIDE")
2584
2585        return self.expression(
2586            exp.LockingProperty,
2587            this=this,
2588            kind=kind,
2589            for_or_in=for_or_in,
2590            lock_type=lock_type,
2591            override=override,
2592        )
2593
2594    def _parse_partition_by(self) -> t.List[exp.Expression]:
2595        if self._match(TokenType.PARTITION_BY):
2596            return self._parse_csv(self._parse_assignment)
2597        return []
2598
2599    def _parse_partition_bound_spec(self) -> exp.PartitionBoundSpec:
2600        def _parse_partition_bound_expr() -> t.Optional[exp.Expression]:
2601            if self._match_text_seq("MINVALUE"):
2602                return exp.var("MINVALUE")
2603            if self._match_text_seq("MAXVALUE"):
2604                return exp.var("MAXVALUE")
2605            return self._parse_bitwise()
2606
2607        this: t.Optional[exp.Expression | t.List[exp.Expression]] = None
2608        expression = None
2609        from_expressions = None
2610        to_expressions = None
2611
2612        if self._match(TokenType.IN):
2613            this = self._parse_wrapped_csv(self._parse_bitwise)
2614        elif self._match(TokenType.FROM):
2615            from_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2616            self._match_text_seq("TO")
2617            to_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2618        elif self._match_text_seq("WITH", "(", "MODULUS"):
2619            this = self._parse_number()
2620            self._match_text_seq(",", "REMAINDER")
2621            expression = self._parse_number()
2622            self._match_r_paren()
2623        else:
2624            self.raise_error("Failed to parse partition bound spec.")
2625
2626        return self.expression(
2627            exp.PartitionBoundSpec,
2628            this=this,
2629            expression=expression,
2630            from_expressions=from_expressions,
2631            to_expressions=to_expressions,
2632        )
2633
2634    # https://www.postgresql.org/docs/current/sql-createtable.html
2635    def _parse_partitioned_of(self) -> t.Optional[exp.PartitionedOfProperty]:
2636        if not self._match_text_seq("OF"):
2637            self._retreat(self._index - 1)
2638            return None
2639
2640        this = self._parse_table(schema=True)
2641
2642        if self._match(TokenType.DEFAULT):
2643            expression: exp.Var | exp.PartitionBoundSpec = exp.var("DEFAULT")
2644        elif self._match_text_seq("FOR", "VALUES"):
2645            expression = self._parse_partition_bound_spec()
2646        else:
2647            self.raise_error("Expecting either DEFAULT or FOR VALUES clause.")
2648
2649        return self.expression(exp.PartitionedOfProperty, this=this, expression=expression)
2650
2651    def _parse_partitioned_by(self) -> exp.PartitionedByProperty:
2652        self._match(TokenType.EQ)
2653        return self.expression(
2654            exp.PartitionedByProperty,
2655            this=self._parse_schema() or self._parse_bracket(self._parse_field()),
2656        )
2657
2658    def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty:
2659        if self._match_text_seq("AND", "STATISTICS"):
2660            statistics = True
2661        elif self._match_text_seq("AND", "NO", "STATISTICS"):
2662            statistics = False
2663        else:
2664            statistics = None
2665
2666        return self.expression(exp.WithDataProperty, no=no, statistics=statistics)
2667
2668    def _parse_contains_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2669        if self._match_text_seq("SQL"):
2670            return self.expression(exp.SqlReadWriteProperty, this="CONTAINS SQL")
2671        return None
2672
2673    def _parse_modifies_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2674        if self._match_text_seq("SQL", "DATA"):
2675            return self.expression(exp.SqlReadWriteProperty, this="MODIFIES SQL DATA")
2676        return None
2677
2678    def _parse_no_property(self) -> t.Optional[exp.Expression]:
2679        if self._match_text_seq("PRIMARY", "INDEX"):
2680            return exp.NoPrimaryIndexProperty()
2681        if self._match_text_seq("SQL"):
2682            return self.expression(exp.SqlReadWriteProperty, this="NO SQL")
2683        return None
2684
2685    def _parse_on_property(self) -> t.Optional[exp.Expression]:
2686        if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"):
2687            return exp.OnCommitProperty()
2688        if self._match_text_seq("COMMIT", "DELETE", "ROWS"):
2689            return exp.OnCommitProperty(delete=True)
2690        return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var()))
2691
2692    def _parse_reads_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2693        if self._match_text_seq("SQL", "DATA"):
2694            return self.expression(exp.SqlReadWriteProperty, this="READS SQL DATA")
2695        return None
2696
2697    def _parse_distkey(self) -> exp.DistKeyProperty:
2698        return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var))
2699
2700    def _parse_create_like(self) -> t.Optional[exp.LikeProperty]:
2701        table = self._parse_table(schema=True)
2702
2703        options = []
2704        while self._match_texts(("INCLUDING", "EXCLUDING")):
2705            this = self._prev.text.upper()
2706
2707            id_var = self._parse_id_var()
2708            if not id_var:
2709                return None
2710
2711            options.append(
2712                self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper()))
2713            )
2714
2715        return self.expression(exp.LikeProperty, this=table, expressions=options)
2716
2717    def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty:
2718        return self.expression(
2719            exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound
2720        )
2721
2722    def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty:
2723        self._match(TokenType.EQ)
2724        return self.expression(
2725            exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default
2726        )
2727
2728    def _parse_remote_with_connection(self) -> exp.RemoteWithConnectionModelProperty:
2729        self._match_text_seq("WITH", "CONNECTION")
2730        return self.expression(
2731            exp.RemoteWithConnectionModelProperty, this=self._parse_table_parts()
2732        )
2733
2734    def _parse_returns(self) -> exp.ReturnsProperty:
2735        value: t.Optional[exp.Expression]
2736        null = None
2737        is_table = self._match(TokenType.TABLE)
2738
2739        if is_table:
2740            if self._match(TokenType.LT):
2741                value = self.expression(
2742                    exp.Schema,
2743                    this="TABLE",
2744                    expressions=self._parse_csv(self._parse_struct_types),
2745                )
2746                if not self._match(TokenType.GT):
2747                    self.raise_error("Expecting >")
2748            else:
2749                value = self._parse_schema(exp.var("TABLE"))
2750        elif self._match_text_seq("NULL", "ON", "NULL", "INPUT"):
2751            null = True
2752            value = None
2753        else:
2754            value = self._parse_types()
2755
2756        return self.expression(exp.ReturnsProperty, this=value, is_table=is_table, null=null)
2757
2758    def _parse_describe(self) -> exp.Describe:
2759        kind = self._match_set(self.CREATABLES) and self._prev.text
2760        style = self._match_texts(self.DESCRIBE_STYLES) and self._prev.text.upper()
2761        if self._match(TokenType.DOT):
2762            style = None
2763            self._retreat(self._index - 2)
2764
2765        format = self._parse_property() if self._match(TokenType.FORMAT, advance=False) else None
2766
2767        if self._match_set(self.STATEMENT_PARSERS, advance=False):
2768            this = self._parse_statement()
2769        else:
2770            this = self._parse_table(schema=True)
2771
2772        properties = self._parse_properties()
2773        expressions = properties.expressions if properties else None
2774        partition = self._parse_partition()
2775        return self.expression(
2776            exp.Describe,
2777            this=this,
2778            style=style,
2779            kind=kind,
2780            expressions=expressions,
2781            partition=partition,
2782            format=format,
2783        )
2784
2785    def _parse_multitable_inserts(self, comments: t.Optional[t.List[str]]) -> exp.MultitableInserts:
2786        kind = self._prev.text.upper()
2787        expressions = []
2788
2789        def parse_conditional_insert() -> t.Optional[exp.ConditionalInsert]:
2790            if self._match(TokenType.WHEN):
2791                expression = self._parse_disjunction()
2792                self._match(TokenType.THEN)
2793            else:
2794                expression = None
2795
2796            else_ = self._match(TokenType.ELSE)
2797
2798            if not self._match(TokenType.INTO):
2799                return None
2800
2801            return self.expression(
2802                exp.ConditionalInsert,
2803                this=self.expression(
2804                    exp.Insert,
2805                    this=self._parse_table(schema=True),
2806                    expression=self._parse_derived_table_values(),
2807                ),
2808                expression=expression,
2809                else_=else_,
2810            )
2811
2812        expression = parse_conditional_insert()
2813        while expression is not None:
2814            expressions.append(expression)
2815            expression = parse_conditional_insert()
2816
2817        return self.expression(
2818            exp.MultitableInserts,
2819            kind=kind,
2820            comments=comments,
2821            expressions=expressions,
2822            source=self._parse_table(),
2823        )
2824
2825    def _parse_insert(self) -> t.Union[exp.Insert, exp.MultitableInserts]:
2826        comments = []
2827        hint = self._parse_hint()
2828        overwrite = self._match(TokenType.OVERWRITE)
2829        ignore = self._match(TokenType.IGNORE)
2830        local = self._match_text_seq("LOCAL")
2831        alternative = None
2832        is_function = None
2833
2834        if self._match_text_seq("DIRECTORY"):
2835            this: t.Optional[exp.Expression] = self.expression(
2836                exp.Directory,
2837                this=self._parse_var_or_string(),
2838                local=local,
2839                row_format=self._parse_row_format(match_row=True),
2840            )
2841        else:
2842            if self._match_set((TokenType.FIRST, TokenType.ALL)):
2843                comments += ensure_list(self._prev_comments)
2844                return self._parse_multitable_inserts(comments)
2845
2846            if self._match(TokenType.OR):
2847                alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text
2848
2849            self._match(TokenType.INTO)
2850            comments += ensure_list(self._prev_comments)
2851            self._match(TokenType.TABLE)
2852            is_function = self._match(TokenType.FUNCTION)
2853
2854            this = (
2855                self._parse_table(schema=True, parse_partition=True)
2856                if not is_function
2857                else self._parse_function()
2858            )
2859            if isinstance(this, exp.Table) and self._match(TokenType.ALIAS, advance=False):
2860                this.set("alias", self._parse_table_alias())
2861
2862        returning = self._parse_returning()
2863
2864        return self.expression(
2865            exp.Insert,
2866            comments=comments,
2867            hint=hint,
2868            is_function=is_function,
2869            this=this,
2870            stored=self._match_text_seq("STORED") and self._parse_stored(),
2871            by_name=self._match_text_seq("BY", "NAME"),
2872            exists=self._parse_exists(),
2873            where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) and self._parse_assignment(),
2874            partition=self._match(TokenType.PARTITION_BY) and self._parse_partitioned_by(),
2875            settings=self._match_text_seq("SETTINGS") and self._parse_settings_property(),
2876            expression=self._parse_derived_table_values() or self._parse_ddl_select(),
2877            conflict=self._parse_on_conflict(),
2878            returning=returning or self._parse_returning(),
2879            overwrite=overwrite,
2880            alternative=alternative,
2881            ignore=ignore,
2882            source=self._match(TokenType.TABLE) and self._parse_table(),
2883        )
2884
2885    def _parse_kill(self) -> exp.Kill:
2886        kind = exp.var(self._prev.text) if self._match_texts(("CONNECTION", "QUERY")) else None
2887
2888        return self.expression(
2889            exp.Kill,
2890            this=self._parse_primary(),
2891            kind=kind,
2892        )
2893
2894    def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]:
2895        conflict = self._match_text_seq("ON", "CONFLICT")
2896        duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY")
2897
2898        if not conflict and not duplicate:
2899            return None
2900
2901        conflict_keys = None
2902        constraint = None
2903
2904        if conflict:
2905            if self._match_text_seq("ON", "CONSTRAINT"):
2906                constraint = self._parse_id_var()
2907            elif self._match(TokenType.L_PAREN):
2908                conflict_keys = self._parse_csv(self._parse_id_var)
2909                self._match_r_paren()
2910
2911        action = self._parse_var_from_options(self.CONFLICT_ACTIONS)
2912        if self._prev.token_type == TokenType.UPDATE:
2913            self._match(TokenType.SET)
2914            expressions = self._parse_csv(self._parse_equality)
2915        else:
2916            expressions = None
2917
2918        return self.expression(
2919            exp.OnConflict,
2920            duplicate=duplicate,
2921            expressions=expressions,
2922            action=action,
2923            conflict_keys=conflict_keys,
2924            constraint=constraint,
2925            where=self._parse_where(),
2926        )
2927
2928    def _parse_returning(self) -> t.Optional[exp.Returning]:
2929        if not self._match(TokenType.RETURNING):
2930            return None
2931        return self.expression(
2932            exp.Returning,
2933            expressions=self._parse_csv(self._parse_expression),
2934            into=self._match(TokenType.INTO) and self._parse_table_part(),
2935        )
2936
2937    def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2938        if not self._match(TokenType.FORMAT):
2939            return None
2940        return self._parse_row_format()
2941
2942    def _parse_serde_properties(self, with_: bool = False) -> t.Optional[exp.SerdeProperties]:
2943        index = self._index
2944        with_ = with_ or self._match_text_seq("WITH")
2945
2946        if not self._match(TokenType.SERDE_PROPERTIES):
2947            self._retreat(index)
2948            return None
2949        return self.expression(
2950            exp.SerdeProperties,
2951            **{  # type: ignore
2952                "expressions": self._parse_wrapped_properties(),
2953                "with": with_,
2954            },
2955        )
2956
2957    def _parse_row_format(
2958        self, match_row: bool = False
2959    ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2960        if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT):
2961            return None
2962
2963        if self._match_text_seq("SERDE"):
2964            this = self._parse_string()
2965
2966            serde_properties = self._parse_serde_properties()
2967
2968            return self.expression(
2969                exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties
2970            )
2971
2972        self._match_text_seq("DELIMITED")
2973
2974        kwargs = {}
2975
2976        if self._match_text_seq("FIELDS", "TERMINATED", "BY"):
2977            kwargs["fields"] = self._parse_string()
2978            if self._match_text_seq("ESCAPED", "BY"):
2979                kwargs["escaped"] = self._parse_string()
2980        if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"):
2981            kwargs["collection_items"] = self._parse_string()
2982        if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"):
2983            kwargs["map_keys"] = self._parse_string()
2984        if self._match_text_seq("LINES", "TERMINATED", "BY"):
2985            kwargs["lines"] = self._parse_string()
2986        if self._match_text_seq("NULL", "DEFINED", "AS"):
2987            kwargs["null"] = self._parse_string()
2988
2989        return self.expression(exp.RowFormatDelimitedProperty, **kwargs)  # type: ignore
2990
2991    def _parse_load(self) -> exp.LoadData | exp.Command:
2992        if self._match_text_seq("DATA"):
2993            local = self._match_text_seq("LOCAL")
2994            self._match_text_seq("INPATH")
2995            inpath = self._parse_string()
2996            overwrite = self._match(TokenType.OVERWRITE)
2997            self._match_pair(TokenType.INTO, TokenType.TABLE)
2998
2999            return self.expression(
3000                exp.LoadData,
3001                this=self._parse_table(schema=True),
3002                local=local,
3003                overwrite=overwrite,
3004                inpath=inpath,
3005                partition=self._parse_partition(),
3006                input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(),
3007                serde=self._match_text_seq("SERDE") and self._parse_string(),
3008            )
3009        return self._parse_as_command(self._prev)
3010
3011    def _parse_delete(self) -> exp.Delete:
3012        # This handles MySQL's "Multiple-Table Syntax"
3013        # https://dev.mysql.com/doc/refman/8.0/en/delete.html
3014        tables = None
3015        if not self._match(TokenType.FROM, advance=False):
3016            tables = self._parse_csv(self._parse_table) or None
3017
3018        returning = self._parse_returning()
3019
3020        return self.expression(
3021            exp.Delete,
3022            tables=tables,
3023            this=self._match(TokenType.FROM) and self._parse_table(joins=True),
3024            using=self._match(TokenType.USING) and self._parse_table(joins=True),
3025            cluster=self._match(TokenType.ON) and self._parse_on_property(),
3026            where=self._parse_where(),
3027            returning=returning or self._parse_returning(),
3028            limit=self._parse_limit(),
3029        )
3030
3031    def _parse_update(self) -> exp.Update:
3032        this = self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS)
3033        expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality)
3034        returning = self._parse_returning()
3035        return self.expression(
3036            exp.Update,
3037            **{  # type: ignore
3038                "this": this,
3039                "expressions": expressions,
3040                "from": self._parse_from(joins=True),
3041                "where": self._parse_where(),
3042                "returning": returning or self._parse_returning(),
3043                "order": self._parse_order(),
3044                "limit": self._parse_limit(),
3045            },
3046        )
3047
3048    def _parse_use(self) -> exp.Use:
3049        return self.expression(
3050            exp.Use,
3051            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
3052            this=self._parse_table(schema=False),
3053        )
3054
3055    def _parse_uncache(self) -> exp.Uncache:
3056        if not self._match(TokenType.TABLE):
3057            self.raise_error("Expecting TABLE after UNCACHE")
3058
3059        return self.expression(
3060            exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True)
3061        )
3062
3063    def _parse_cache(self) -> exp.Cache:
3064        lazy = self._match_text_seq("LAZY")
3065        self._match(TokenType.TABLE)
3066        table = self._parse_table(schema=True)
3067
3068        options = []
3069        if self._match_text_seq("OPTIONS"):
3070            self._match_l_paren()
3071            k = self._parse_string()
3072            self._match(TokenType.EQ)
3073            v = self._parse_string()
3074            options = [k, v]
3075            self._match_r_paren()
3076
3077        self._match(TokenType.ALIAS)
3078        return self.expression(
3079            exp.Cache,
3080            this=table,
3081            lazy=lazy,
3082            options=options,
3083            expression=self._parse_select(nested=True),
3084        )
3085
3086    def _parse_partition(self) -> t.Optional[exp.Partition]:
3087        if not self._match_texts(self.PARTITION_KEYWORDS):
3088            return None
3089
3090        return self.expression(
3091            exp.Partition,
3092            subpartition=self._prev.text.upper() == "SUBPARTITION",
3093            expressions=self._parse_wrapped_csv(self._parse_assignment),
3094        )
3095
3096    def _parse_value(self, values: bool = True) -> t.Optional[exp.Tuple]:
3097        def _parse_value_expression() -> t.Optional[exp.Expression]:
3098            if self.dialect.SUPPORTS_VALUES_DEFAULT and self._match(TokenType.DEFAULT):
3099                return exp.var(self._prev.text.upper())
3100            return self._parse_expression()
3101
3102        if self._match(TokenType.L_PAREN):
3103            expressions = self._parse_csv(_parse_value_expression)
3104            self._match_r_paren()
3105            return self.expression(exp.Tuple, expressions=expressions)
3106
3107        # In some dialects we can have VALUES 1, 2 which results in 1 column & 2 rows.
3108        expression = self._parse_expression()
3109        if expression:
3110            return self.expression(exp.Tuple, expressions=[expression])
3111        return None
3112
3113    def _parse_projections(self) -> t.List[exp.Expression]:
3114        return self._parse_expressions()
3115
3116    def _parse_wrapped_select(self, table: bool = False) -> t.Optional[exp.Expression]:
3117        if self._match_set((TokenType.PIVOT, TokenType.UNPIVOT)):
3118            this: t.Optional[exp.Expression] = self._parse_simplified_pivot(
3119                is_unpivot=self._prev.token_type == TokenType.UNPIVOT
3120            )
3121        elif self._match(TokenType.FROM):
3122            from_ = self._parse_from(skip_from_token=True)
3123            # Support parentheses for duckdb FROM-first syntax
3124            select = self._parse_select()
3125            if select:
3126                select.set("from", from_)
3127                this = select
3128            else:
3129                this = exp.select("*").from_(t.cast(exp.From, from_))
3130        else:
3131            this = (
3132                self._parse_table()
3133                if table
3134                else self._parse_select(nested=True, parse_set_operation=False)
3135            )
3136
3137            # Transform exp.Values into a exp.Table to pass through parse_query_modifiers
3138            # in case a modifier (e.g. join) is following
3139            if table and isinstance(this, exp.Values) and this.alias:
3140                alias = this.args["alias"].pop()
3141                this = exp.Table(this=this, alias=alias)
3142
3143            this = self._parse_query_modifiers(self._parse_set_operations(this))
3144
3145        return this
3146
3147    def _parse_select(
3148        self,
3149        nested: bool = False,
3150        table: bool = False,
3151        parse_subquery_alias: bool = True,
3152        parse_set_operation: bool = True,
3153    ) -> t.Optional[exp.Expression]:
3154        cte = self._parse_with()
3155
3156        if cte:
3157            this = self._parse_statement()
3158
3159            if not this:
3160                self.raise_error("Failed to parse any statement following CTE")
3161                return cte
3162
3163            if "with" in this.arg_types:
3164                this.set("with", cte)
3165            else:
3166                self.raise_error(f"{this.key} does not support CTE")
3167                this = cte
3168
3169            return this
3170
3171        # duckdb supports leading with FROM x
3172        from_ = self._parse_from() if self._match(TokenType.FROM, advance=False) else None
3173
3174        if self._match(TokenType.SELECT):
3175            comments = self._prev_comments
3176
3177            hint = self._parse_hint()
3178
3179            if self._next and not self._next.token_type == TokenType.DOT:
3180                all_ = self._match(TokenType.ALL)
3181                distinct = self._match_set(self.DISTINCT_TOKENS)
3182            else:
3183                all_, distinct = None, None
3184
3185            kind = (
3186                self._match(TokenType.ALIAS)
3187                and self._match_texts(("STRUCT", "VALUE"))
3188                and self._prev.text.upper()
3189            )
3190
3191            if distinct:
3192                distinct = self.expression(
3193                    exp.Distinct,
3194                    on=self._parse_value(values=False) if self._match(TokenType.ON) else None,
3195                )
3196
3197            if all_ and distinct:
3198                self.raise_error("Cannot specify both ALL and DISTINCT after SELECT")
3199
3200            operation_modifiers = []
3201            while self._curr and self._match_texts(self.OPERATION_MODIFIERS):
3202                operation_modifiers.append(exp.var(self._prev.text.upper()))
3203
3204            limit = self._parse_limit(top=True)
3205            projections = self._parse_projections()
3206
3207            this = self.expression(
3208                exp.Select,
3209                kind=kind,
3210                hint=hint,
3211                distinct=distinct,
3212                expressions=projections,
3213                limit=limit,
3214                operation_modifiers=operation_modifiers or None,
3215            )
3216            this.comments = comments
3217
3218            into = self._parse_into()
3219            if into:
3220                this.set("into", into)
3221
3222            if not from_:
3223                from_ = self._parse_from()
3224
3225            if from_:
3226                this.set("from", from_)
3227
3228            this = self._parse_query_modifiers(this)
3229        elif (table or nested) and self._match(TokenType.L_PAREN):
3230            this = self._parse_wrapped_select(table=table)
3231
3232            # We return early here so that the UNION isn't attached to the subquery by the
3233            # following call to _parse_set_operations, but instead becomes the parent node
3234            self._match_r_paren()
3235            return self._parse_subquery(this, parse_alias=parse_subquery_alias)
3236        elif self._match(TokenType.VALUES, advance=False):
3237            this = self._parse_derived_table_values()
3238        elif from_:
3239            this = exp.select("*").from_(from_.this, copy=False)
3240        elif self._match(TokenType.SUMMARIZE):
3241            table = self._match(TokenType.TABLE)
3242            this = self._parse_select() or self._parse_string() or self._parse_table()
3243            return self.expression(exp.Summarize, this=this, table=table)
3244        elif self._match(TokenType.DESCRIBE):
3245            this = self._parse_describe()
3246        elif self._match_text_seq("STREAM"):
3247            this = self._parse_function()
3248            if this:
3249                this = self.expression(exp.Stream, this=this)
3250            else:
3251                self._retreat(self._index - 1)
3252        else:
3253            this = None
3254
3255        return self._parse_set_operations(this) if parse_set_operation else this
3256
3257    def _parse_recursive_with_search(self) -> t.Optional[exp.RecursiveWithSearch]:
3258        self._match_text_seq("SEARCH")
3259
3260        kind = self._match_texts(self.RECURSIVE_CTE_SEARCH_KIND) and self._prev.text.upper()
3261
3262        if not kind:
3263            return None
3264
3265        self._match_text_seq("FIRST", "BY")
3266
3267        return self.expression(
3268            exp.RecursiveWithSearch,
3269            kind=kind,
3270            this=self._parse_id_var(),
3271            expression=self._match_text_seq("SET") and self._parse_id_var(),
3272            using=self._match_text_seq("USING") and self._parse_id_var(),
3273        )
3274
3275    def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]:
3276        if not skip_with_token and not self._match(TokenType.WITH):
3277            return None
3278
3279        comments = self._prev_comments
3280        recursive = self._match(TokenType.RECURSIVE)
3281
3282        last_comments = None
3283        expressions = []
3284        while True:
3285            cte = self._parse_cte()
3286            if isinstance(cte, exp.CTE):
3287                expressions.append(cte)
3288                if last_comments:
3289                    cte.add_comments(last_comments)
3290
3291            if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH):
3292                break
3293            else:
3294                self._match(TokenType.WITH)
3295
3296            last_comments = self._prev_comments
3297
3298        return self.expression(
3299            exp.With,
3300            comments=comments,
3301            expressions=expressions,
3302            recursive=recursive,
3303            search=self._parse_recursive_with_search(),
3304        )
3305
3306    def _parse_cte(self) -> t.Optional[exp.CTE]:
3307        index = self._index
3308
3309        alias = self._parse_table_alias(self.ID_VAR_TOKENS)
3310        if not alias or not alias.this:
3311            self.raise_error("Expected CTE to have alias")
3312
3313        if not self._match(TokenType.ALIAS) and not self.OPTIONAL_ALIAS_TOKEN_CTE:
3314            self._retreat(index)
3315            return None
3316
3317        comments = self._prev_comments
3318
3319        if self._match_text_seq("NOT", "MATERIALIZED"):
3320            materialized = False
3321        elif self._match_text_seq("MATERIALIZED"):
3322            materialized = True
3323        else:
3324            materialized = None
3325
3326        cte = self.expression(
3327            exp.CTE,
3328            this=self._parse_wrapped(self._parse_statement),
3329            alias=alias,
3330            materialized=materialized,
3331            comments=comments,
3332        )
3333
3334        if isinstance(cte.this, exp.Values):
3335            cte.set("this", exp.select("*").from_(exp.alias_(cte.this, "_values", table=True)))
3336
3337        return cte
3338
3339    def _parse_table_alias(
3340        self, alias_tokens: t.Optional[t.Collection[TokenType]] = None
3341    ) -> t.Optional[exp.TableAlias]:
3342        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
3343        # so this section tries to parse the clause version and if it fails, it treats the token
3344        # as an identifier (alias)
3345        if self._can_parse_limit_or_offset():
3346            return None
3347
3348        any_token = self._match(TokenType.ALIAS)
3349        alias = (
3350            self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3351            or self._parse_string_as_identifier()
3352        )
3353
3354        index = self._index
3355        if self._match(TokenType.L_PAREN):
3356            columns = self._parse_csv(self._parse_function_parameter)
3357            self._match_r_paren() if columns else self._retreat(index)
3358        else:
3359            columns = None
3360
3361        if not alias and not columns:
3362            return None
3363
3364        table_alias = self.expression(exp.TableAlias, this=alias, columns=columns)
3365
3366        # We bubble up comments from the Identifier to the TableAlias
3367        if isinstance(alias, exp.Identifier):
3368            table_alias.add_comments(alias.pop_comments())
3369
3370        return table_alias
3371
3372    def _parse_subquery(
3373        self, this: t.Optional[exp.Expression], parse_alias: bool = True
3374    ) -> t.Optional[exp.Subquery]:
3375        if not this:
3376            return None
3377
3378        return self.expression(
3379            exp.Subquery,
3380            this=this,
3381            pivots=self._parse_pivots(),
3382            alias=self._parse_table_alias() if parse_alias else None,
3383            sample=self._parse_table_sample(),
3384        )
3385
3386    def _implicit_unnests_to_explicit(self, this: E) -> E:
3387        from sqlglot.optimizer.normalize_identifiers import normalize_identifiers as _norm
3388
3389        refs = {_norm(this.args["from"].this.copy(), dialect=self.dialect).alias_or_name}
3390        for i, join in enumerate(this.args.get("joins") or []):
3391            table = join.this
3392            normalized_table = table.copy()
3393            normalized_table.meta["maybe_column"] = True
3394            normalized_table = _norm(normalized_table, dialect=self.dialect)
3395
3396            if isinstance(table, exp.Table) and not join.args.get("on"):
3397                if normalized_table.parts[0].name in refs:
3398                    table_as_column = table.to_column()
3399                    unnest = exp.Unnest(expressions=[table_as_column])
3400
3401                    # Table.to_column creates a parent Alias node that we want to convert to
3402                    # a TableAlias and attach to the Unnest, so it matches the parser's output
3403                    if isinstance(table.args.get("alias"), exp.TableAlias):
3404                        table_as_column.replace(table_as_column.this)
3405                        exp.alias_(unnest, None, table=[table.args["alias"].this], copy=False)
3406
3407                    table.replace(unnest)
3408
3409            refs.add(normalized_table.alias_or_name)
3410
3411        return this
3412
3413    def _parse_query_modifiers(
3414        self, this: t.Optional[exp.Expression]
3415    ) -> t.Optional[exp.Expression]:
3416        if isinstance(this, self.MODIFIABLES):
3417            for join in self._parse_joins():
3418                this.append("joins", join)
3419            for lateral in iter(self._parse_lateral, None):
3420                this.append("laterals", lateral)
3421
3422            while True:
3423                if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False):
3424                    parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type]
3425                    key, expression = parser(self)
3426
3427                    if expression:
3428                        this.set(key, expression)
3429                        if key == "limit":
3430                            offset = expression.args.pop("offset", None)
3431
3432                            if offset:
3433                                offset = exp.Offset(expression=offset)
3434                                this.set("offset", offset)
3435
3436                                limit_by_expressions = expression.expressions
3437                                expression.set("expressions", None)
3438                                offset.set("expressions", limit_by_expressions)
3439                        continue
3440                break
3441
3442        if self.SUPPORTS_IMPLICIT_UNNEST and this and this.args.get("from"):
3443            this = self._implicit_unnests_to_explicit(this)
3444
3445        return this
3446
3447    def _parse_hint_fallback_to_string(self) -> t.Optional[exp.Hint]:
3448        start = self._curr
3449        while self._curr:
3450            self._advance()
3451
3452        end = self._tokens[self._index - 1]
3453        return exp.Hint(expressions=[self._find_sql(start, end)])
3454
3455    def _parse_hint_function_call(self) -> t.Optional[exp.Expression]:
3456        return self._parse_function_call()
3457
3458    def _parse_hint_body(self) -> t.Optional[exp.Hint]:
3459        start_index = self._index
3460        should_fallback_to_string = False
3461
3462        hints = []
3463        try:
3464            for hint in iter(
3465                lambda: self._parse_csv(
3466                    lambda: self._parse_hint_function_call() or self._parse_var(upper=True),
3467                ),
3468                [],
3469            ):
3470                hints.extend(hint)
3471        except ParseError:
3472            should_fallback_to_string = True
3473
3474        if should_fallback_to_string or self._curr:
3475            self._retreat(start_index)
3476            return self._parse_hint_fallback_to_string()
3477
3478        return self.expression(exp.Hint, expressions=hints)
3479
3480    def _parse_hint(self) -> t.Optional[exp.Hint]:
3481        if self._match(TokenType.HINT) and self._prev_comments:
3482            return exp.maybe_parse(self._prev_comments[0], into=exp.Hint, dialect=self.dialect)
3483
3484        return None
3485
3486    def _parse_into(self) -> t.Optional[exp.Into]:
3487        if not self._match(TokenType.INTO):
3488            return None
3489
3490        temp = self._match(TokenType.TEMPORARY)
3491        unlogged = self._match_text_seq("UNLOGGED")
3492        self._match(TokenType.TABLE)
3493
3494        return self.expression(
3495            exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged
3496        )
3497
3498    def _parse_from(
3499        self, joins: bool = False, skip_from_token: bool = False
3500    ) -> t.Optional[exp.From]:
3501        if not skip_from_token and not self._match(TokenType.FROM):
3502            return None
3503
3504        return self.expression(
3505            exp.From, comments=self._prev_comments, this=self._parse_table(joins=joins)
3506        )
3507
3508    def _parse_match_recognize_measure(self) -> exp.MatchRecognizeMeasure:
3509        return self.expression(
3510            exp.MatchRecognizeMeasure,
3511            window_frame=self._match_texts(("FINAL", "RUNNING")) and self._prev.text.upper(),
3512            this=self._parse_expression(),
3513        )
3514
3515    def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]:
3516        if not self._match(TokenType.MATCH_RECOGNIZE):
3517            return None
3518
3519        self._match_l_paren()
3520
3521        partition = self._parse_partition_by()
3522        order = self._parse_order()
3523
3524        measures = (
3525            self._parse_csv(self._parse_match_recognize_measure)
3526            if self._match_text_seq("MEASURES")
3527            else None
3528        )
3529
3530        if self._match_text_seq("ONE", "ROW", "PER", "MATCH"):
3531            rows = exp.var("ONE ROW PER MATCH")
3532        elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"):
3533            text = "ALL ROWS PER MATCH"
3534            if self._match_text_seq("SHOW", "EMPTY", "MATCHES"):
3535                text += " SHOW EMPTY MATCHES"
3536            elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"):
3537                text += " OMIT EMPTY MATCHES"
3538            elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"):
3539                text += " WITH UNMATCHED ROWS"
3540            rows = exp.var(text)
3541        else:
3542            rows = None
3543
3544        if self._match_text_seq("AFTER", "MATCH", "SKIP"):
3545            text = "AFTER MATCH SKIP"
3546            if self._match_text_seq("PAST", "LAST", "ROW"):
3547                text += " PAST LAST ROW"
3548            elif self._match_text_seq("TO", "NEXT", "ROW"):
3549                text += " TO NEXT ROW"
3550            elif self._match_text_seq("TO", "FIRST"):
3551                text += f" TO FIRST {self._advance_any().text}"  # type: ignore
3552            elif self._match_text_seq("TO", "LAST"):
3553                text += f" TO LAST {self._advance_any().text}"  # type: ignore
3554            after = exp.var(text)
3555        else:
3556            after = None
3557
3558        if self._match_text_seq("PATTERN"):
3559            self._match_l_paren()
3560
3561            if not self._curr:
3562                self.raise_error("Expecting )", self._curr)
3563
3564            paren = 1
3565            start = self._curr
3566
3567            while self._curr and paren > 0:
3568                if self._curr.token_type == TokenType.L_PAREN:
3569                    paren += 1
3570                if self._curr.token_type == TokenType.R_PAREN:
3571                    paren -= 1
3572
3573                end = self._prev
3574                self._advance()
3575
3576            if paren > 0:
3577                self.raise_error("Expecting )", self._curr)
3578
3579            pattern = exp.var(self._find_sql(start, end))
3580        else:
3581            pattern = None
3582
3583        define = (
3584            self._parse_csv(self._parse_name_as_expression)
3585            if self._match_text_seq("DEFINE")
3586            else None
3587        )
3588
3589        self._match_r_paren()
3590
3591        return self.expression(
3592            exp.MatchRecognize,
3593            partition_by=partition,
3594            order=order,
3595            measures=measures,
3596            rows=rows,
3597            after=after,
3598            pattern=pattern,
3599            define=define,
3600            alias=self._parse_table_alias(),
3601        )
3602
3603    def _parse_lateral(self) -> t.Optional[exp.Lateral]:
3604        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY)
3605        if not cross_apply and self._match_pair(TokenType.OUTER, TokenType.APPLY):
3606            cross_apply = False
3607
3608        if cross_apply is not None:
3609            this = self._parse_select(table=True)
3610            view = None
3611            outer = None
3612        elif self._match(TokenType.LATERAL):
3613            this = self._parse_select(table=True)
3614            view = self._match(TokenType.VIEW)
3615            outer = self._match(TokenType.OUTER)
3616        else:
3617            return None
3618
3619        if not this:
3620            this = (
3621                self._parse_unnest()
3622                or self._parse_function()
3623                or self._parse_id_var(any_token=False)
3624            )
3625
3626            while self._match(TokenType.DOT):
3627                this = exp.Dot(
3628                    this=this,
3629                    expression=self._parse_function() or self._parse_id_var(any_token=False),
3630                )
3631
3632        ordinality: t.Optional[bool] = None
3633
3634        if view:
3635            table = self._parse_id_var(any_token=False)
3636            columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else []
3637            table_alias: t.Optional[exp.TableAlias] = self.expression(
3638                exp.TableAlias, this=table, columns=columns
3639            )
3640        elif isinstance(this, (exp.Subquery, exp.Unnest)) and this.alias:
3641            # We move the alias from the lateral's child node to the lateral itself
3642            table_alias = this.args["alias"].pop()
3643        else:
3644            ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
3645            table_alias = self._parse_table_alias()
3646
3647        return self.expression(
3648            exp.Lateral,
3649            this=this,
3650            view=view,
3651            outer=outer,
3652            alias=table_alias,
3653            cross_apply=cross_apply,
3654            ordinality=ordinality,
3655        )
3656
3657    def _parse_join_parts(
3658        self,
3659    ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]:
3660        return (
3661            self._match_set(self.JOIN_METHODS) and self._prev,
3662            self._match_set(self.JOIN_SIDES) and self._prev,
3663            self._match_set(self.JOIN_KINDS) and self._prev,
3664        )
3665
3666    def _parse_using_identifiers(self) -> t.List[exp.Expression]:
3667        def _parse_column_as_identifier() -> t.Optional[exp.Expression]:
3668            this = self._parse_column()
3669            if isinstance(this, exp.Column):
3670                return this.this
3671            return this
3672
3673        return self._parse_wrapped_csv(_parse_column_as_identifier, optional=True)
3674
3675    def _parse_join(
3676        self, skip_join_token: bool = False, parse_bracket: bool = False
3677    ) -> t.Optional[exp.Join]:
3678        if self._match(TokenType.COMMA):
3679            table = self._try_parse(self._parse_table)
3680            if table:
3681                return self.expression(exp.Join, this=table)
3682            return None
3683
3684        index = self._index
3685        method, side, kind = self._parse_join_parts()
3686        hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None
3687        join = self._match(TokenType.JOIN) or (kind and kind.token_type == TokenType.STRAIGHT_JOIN)
3688
3689        if not skip_join_token and not join:
3690            self._retreat(index)
3691            kind = None
3692            method = None
3693            side = None
3694
3695        outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False)
3696        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False)
3697
3698        if not skip_join_token and not join and not outer_apply and not cross_apply:
3699            return None
3700
3701        kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)}
3702        if kind and kind.token_type == TokenType.ARRAY and self._match(TokenType.COMMA):
3703            kwargs["expressions"] = self._parse_csv(
3704                lambda: self._parse_table(parse_bracket=parse_bracket)
3705            )
3706
3707        if method:
3708            kwargs["method"] = method.text
3709        if side:
3710            kwargs["side"] = side.text
3711        if kind:
3712            kwargs["kind"] = kind.text
3713        if hint:
3714            kwargs["hint"] = hint
3715
3716        if self._match(TokenType.MATCH_CONDITION):
3717            kwargs["match_condition"] = self._parse_wrapped(self._parse_comparison)
3718
3719        if self._match(TokenType.ON):
3720            kwargs["on"] = self._parse_assignment()
3721        elif self._match(TokenType.USING):
3722            kwargs["using"] = self._parse_using_identifiers()
3723        elif (
3724            not (outer_apply or cross_apply)
3725            and not isinstance(kwargs["this"], exp.Unnest)
3726            and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
3727        ):
3728            index = self._index
3729            joins: t.Optional[list] = list(self._parse_joins())
3730
3731            if joins and self._match(TokenType.ON):
3732                kwargs["on"] = self._parse_assignment()
3733            elif joins and self._match(TokenType.USING):
3734                kwargs["using"] = self._parse_using_identifiers()
3735            else:
3736                joins = None
3737                self._retreat(index)
3738
3739            kwargs["this"].set("joins", joins if joins else None)
3740
3741        kwargs["pivots"] = self._parse_pivots()
3742
3743        comments = [c for token in (method, side, kind) if token for c in token.comments]
3744        return self.expression(exp.Join, comments=comments, **kwargs)
3745
3746    def _parse_opclass(self) -> t.Optional[exp.Expression]:
3747        this = self._parse_assignment()
3748
3749        if self._match_texts(self.OPCLASS_FOLLOW_KEYWORDS, advance=False):
3750            return this
3751
3752        if not self._match_set(self.OPTYPE_FOLLOW_TOKENS, advance=False):
3753            return self.expression(exp.Opclass, this=this, expression=self._parse_table_parts())
3754
3755        return this
3756
3757    def _parse_index_params(self) -> exp.IndexParameters:
3758        using = self._parse_var(any_token=True) if self._match(TokenType.USING) else None
3759
3760        if self._match(TokenType.L_PAREN, advance=False):
3761            columns = self._parse_wrapped_csv(self._parse_with_operator)
3762        else:
3763            columns = None
3764
3765        include = self._parse_wrapped_id_vars() if self._match_text_seq("INCLUDE") else None
3766        partition_by = self._parse_partition_by()
3767        with_storage = self._match(TokenType.WITH) and self._parse_wrapped_properties()
3768        tablespace = (
3769            self._parse_var(any_token=True)
3770            if self._match_text_seq("USING", "INDEX", "TABLESPACE")
3771            else None
3772        )
3773        where = self._parse_where()
3774
3775        on = self._parse_field() if self._match(TokenType.ON) else None
3776
3777        return self.expression(
3778            exp.IndexParameters,
3779            using=using,
3780            columns=columns,
3781            include=include,
3782            partition_by=partition_by,
3783            where=where,
3784            with_storage=with_storage,
3785            tablespace=tablespace,
3786            on=on,
3787        )
3788
3789    def _parse_index(
3790        self, index: t.Optional[exp.Expression] = None, anonymous: bool = False
3791    ) -> t.Optional[exp.Index]:
3792        if index or anonymous:
3793            unique = None
3794            primary = None
3795            amp = None
3796
3797            self._match(TokenType.ON)
3798            self._match(TokenType.TABLE)  # hive
3799            table = self._parse_table_parts(schema=True)
3800        else:
3801            unique = self._match(TokenType.UNIQUE)
3802            primary = self._match_text_seq("PRIMARY")
3803            amp = self._match_text_seq("AMP")
3804
3805            if not self._match(TokenType.INDEX):
3806                return None
3807
3808            index = self._parse_id_var()
3809            table = None
3810
3811        params = self._parse_index_params()
3812
3813        return self.expression(
3814            exp.Index,
3815            this=index,
3816            table=table,
3817            unique=unique,
3818            primary=primary,
3819            amp=amp,
3820            params=params,
3821        )
3822
3823    def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]:
3824        hints: t.List[exp.Expression] = []
3825        if self._match_pair(TokenType.WITH, TokenType.L_PAREN):
3826            # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16
3827            hints.append(
3828                self.expression(
3829                    exp.WithTableHint,
3830                    expressions=self._parse_csv(
3831                        lambda: self._parse_function() or self._parse_var(any_token=True)
3832                    ),
3833                )
3834            )
3835            self._match_r_paren()
3836        else:
3837            # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html
3838            while self._match_set(self.TABLE_INDEX_HINT_TOKENS):
3839                hint = exp.IndexTableHint(this=self._prev.text.upper())
3840
3841                self._match_set((TokenType.INDEX, TokenType.KEY))
3842                if self._match(TokenType.FOR):
3843                    hint.set("target", self._advance_any() and self._prev.text.upper())
3844
3845                hint.set("expressions", self._parse_wrapped_id_vars())
3846                hints.append(hint)
3847
3848        return hints or None
3849
3850    def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]:
3851        return (
3852            (not schema and self._parse_function(optional_parens=False))
3853            or self._parse_id_var(any_token=False)
3854            or self._parse_string_as_identifier()
3855            or self._parse_placeholder()
3856        )
3857
3858    def _parse_table_parts(
3859        self, schema: bool = False, is_db_reference: bool = False, wildcard: bool = False
3860    ) -> exp.Table:
3861        catalog = None
3862        db = None
3863        table: t.Optional[exp.Expression | str] = self._parse_table_part(schema=schema)
3864
3865        while self._match(TokenType.DOT):
3866            if catalog:
3867                # This allows nesting the table in arbitrarily many dot expressions if needed
3868                table = self.expression(
3869                    exp.Dot, this=table, expression=self._parse_table_part(schema=schema)
3870                )
3871            else:
3872                catalog = db
3873                db = table
3874                # "" used for tsql FROM a..b case
3875                table = self._parse_table_part(schema=schema) or ""
3876
3877        if (
3878            wildcard
3879            and self._is_connected()
3880            and (isinstance(table, exp.Identifier) or not table)
3881            and self._match(TokenType.STAR)
3882        ):
3883            if isinstance(table, exp.Identifier):
3884                table.args["this"] += "*"
3885            else:
3886                table = exp.Identifier(this="*")
3887
3888        # We bubble up comments from the Identifier to the Table
3889        comments = table.pop_comments() if isinstance(table, exp.Expression) else None
3890
3891        if is_db_reference:
3892            catalog = db
3893            db = table
3894            table = None
3895
3896        if not table and not is_db_reference:
3897            self.raise_error(f"Expected table name but got {self._curr}")
3898        if not db and is_db_reference:
3899            self.raise_error(f"Expected database name but got {self._curr}")
3900
3901        table = self.expression(
3902            exp.Table,
3903            comments=comments,
3904            this=table,
3905            db=db,
3906            catalog=catalog,
3907        )
3908
3909        changes = self._parse_changes()
3910        if changes:
3911            table.set("changes", changes)
3912
3913        at_before = self._parse_historical_data()
3914        if at_before:
3915            table.set("when", at_before)
3916
3917        pivots = self._parse_pivots()
3918        if pivots:
3919            table.set("pivots", pivots)
3920
3921        return table
3922
3923    def _parse_table(
3924        self,
3925        schema: bool = False,
3926        joins: bool = False,
3927        alias_tokens: t.Optional[t.Collection[TokenType]] = None,
3928        parse_bracket: bool = False,
3929        is_db_reference: bool = False,
3930        parse_partition: bool = False,
3931    ) -> t.Optional[exp.Expression]:
3932        lateral = self._parse_lateral()
3933        if lateral:
3934            return lateral
3935
3936        unnest = self._parse_unnest()
3937        if unnest:
3938            return unnest
3939
3940        values = self._parse_derived_table_values()
3941        if values:
3942            return values
3943
3944        subquery = self._parse_select(table=True)
3945        if subquery:
3946            if not subquery.args.get("pivots"):
3947                subquery.set("pivots", self._parse_pivots())
3948            return subquery
3949
3950        bracket = parse_bracket and self._parse_bracket(None)
3951        bracket = self.expression(exp.Table, this=bracket) if bracket else None
3952
3953        rows_from = self._match_text_seq("ROWS", "FROM") and self._parse_wrapped_csv(
3954            self._parse_table
3955        )
3956        rows_from = self.expression(exp.Table, rows_from=rows_from) if rows_from else None
3957
3958        only = self._match(TokenType.ONLY)
3959
3960        this = t.cast(
3961            exp.Expression,
3962            bracket
3963            or rows_from
3964            or self._parse_bracket(
3965                self._parse_table_parts(schema=schema, is_db_reference=is_db_reference)
3966            ),
3967        )
3968
3969        if only:
3970            this.set("only", only)
3971
3972        # Postgres supports a wildcard (table) suffix operator, which is a no-op in this context
3973        self._match_text_seq("*")
3974
3975        parse_partition = parse_partition or self.SUPPORTS_PARTITION_SELECTION
3976        if parse_partition and self._match(TokenType.PARTITION, advance=False):
3977            this.set("partition", self._parse_partition())
3978
3979        if schema:
3980            return self._parse_schema(this=this)
3981
3982        version = self._parse_version()
3983
3984        if version:
3985            this.set("version", version)
3986
3987        if self.dialect.ALIAS_POST_TABLESAMPLE:
3988            this.set("sample", self._parse_table_sample())
3989
3990        alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3991        if alias:
3992            this.set("alias", alias)
3993
3994        if isinstance(this, exp.Table) and self._match_text_seq("AT"):
3995            return self.expression(
3996                exp.AtIndex, this=this.to_column(copy=False), expression=self._parse_id_var()
3997            )
3998
3999        this.set("hints", self._parse_table_hints())
4000
4001        if not this.args.get("pivots"):
4002            this.set("pivots", self._parse_pivots())
4003
4004        if not self.dialect.ALIAS_POST_TABLESAMPLE:
4005            this.set("sample", self._parse_table_sample())
4006
4007        if joins:
4008            for join in self._parse_joins():
4009                this.append("joins", join)
4010
4011        if self._match_pair(TokenType.WITH, TokenType.ORDINALITY):
4012            this.set("ordinality", True)
4013            this.set("alias", self._parse_table_alias())
4014
4015        return this
4016
4017    def _parse_version(self) -> t.Optional[exp.Version]:
4018        if self._match(TokenType.TIMESTAMP_SNAPSHOT):
4019            this = "TIMESTAMP"
4020        elif self._match(TokenType.VERSION_SNAPSHOT):
4021            this = "VERSION"
4022        else:
4023            return None
4024
4025        if self._match_set((TokenType.FROM, TokenType.BETWEEN)):
4026            kind = self._prev.text.upper()
4027            start = self._parse_bitwise()
4028            self._match_texts(("TO", "AND"))
4029            end = self._parse_bitwise()
4030            expression: t.Optional[exp.Expression] = self.expression(
4031                exp.Tuple, expressions=[start, end]
4032            )
4033        elif self._match_text_seq("CONTAINED", "IN"):
4034            kind = "CONTAINED IN"
4035            expression = self.expression(
4036                exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise)
4037            )
4038        elif self._match(TokenType.ALL):
4039            kind = "ALL"
4040            expression = None
4041        else:
4042            self._match_text_seq("AS", "OF")
4043            kind = "AS OF"
4044            expression = self._parse_type()
4045
4046        return self.expression(exp.Version, this=this, expression=expression, kind=kind)
4047
4048    def _parse_historical_data(self) -> t.Optional[exp.HistoricalData]:
4049        # https://docs.snowflake.com/en/sql-reference/constructs/at-before
4050        index = self._index
4051        historical_data = None
4052        if self._match_texts(self.HISTORICAL_DATA_PREFIX):
4053            this = self._prev.text.upper()
4054            kind = (
4055                self._match(TokenType.L_PAREN)
4056                and self._match_texts(self.HISTORICAL_DATA_KIND)
4057                and self._prev.text.upper()
4058            )
4059            expression = self._match(TokenType.FARROW) and self._parse_bitwise()
4060
4061            if expression:
4062                self._match_r_paren()
4063                historical_data = self.expression(
4064                    exp.HistoricalData, this=this, kind=kind, expression=expression
4065                )
4066            else:
4067                self._retreat(index)
4068
4069        return historical_data
4070
4071    def _parse_changes(self) -> t.Optional[exp.Changes]:
4072        if not self._match_text_seq("CHANGES", "(", "INFORMATION", "=>"):
4073            return None
4074
4075        information = self._parse_var(any_token=True)
4076        self._match_r_paren()
4077
4078        return self.expression(
4079            exp.Changes,
4080            information=information,
4081            at_before=self._parse_historical_data(),
4082            end=self._parse_historical_data(),
4083        )
4084
4085    def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]:
4086        if not self._match(TokenType.UNNEST):
4087            return None
4088
4089        expressions = self._parse_wrapped_csv(self._parse_equality)
4090        offset = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
4091
4092        alias = self._parse_table_alias() if with_alias else None
4093
4094        if alias:
4095            if self.dialect.UNNEST_COLUMN_ONLY:
4096                if alias.args.get("columns"):
4097                    self.raise_error("Unexpected extra column alias in unnest.")
4098
4099                alias.set("columns", [alias.this])
4100                alias.set("this", None)
4101
4102            columns = alias.args.get("columns") or []
4103            if offset and len(expressions) < len(columns):
4104                offset = columns.pop()
4105
4106        if not offset and self._match_pair(TokenType.WITH, TokenType.OFFSET):
4107            self._match(TokenType.ALIAS)
4108            offset = self._parse_id_var(
4109                any_token=False, tokens=self.UNNEST_OFFSET_ALIAS_TOKENS
4110            ) or exp.to_identifier("offset")
4111
4112        return self.expression(exp.Unnest, expressions=expressions, alias=alias, offset=offset)
4113
4114    def _parse_derived_table_values(self) -> t.Optional[exp.Values]:
4115        is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES)
4116        if not is_derived and not (
4117            # ClickHouse's `FORMAT Values` is equivalent to `VALUES`
4118            self._match_text_seq("VALUES") or self._match_text_seq("FORMAT", "VALUES")
4119        ):
4120            return None
4121
4122        expressions = self._parse_csv(self._parse_value)
4123        alias = self._parse_table_alias()
4124
4125        if is_derived:
4126            self._match_r_paren()
4127
4128        return self.expression(
4129            exp.Values, expressions=expressions, alias=alias or self._parse_table_alias()
4130        )
4131
4132    def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]:
4133        if not self._match(TokenType.TABLE_SAMPLE) and not (
4134            as_modifier and self._match_text_seq("USING", "SAMPLE")
4135        ):
4136            return None
4137
4138        bucket_numerator = None
4139        bucket_denominator = None
4140        bucket_field = None
4141        percent = None
4142        size = None
4143        seed = None
4144
4145        method = self._parse_var(tokens=(TokenType.ROW,), upper=True)
4146        matched_l_paren = self._match(TokenType.L_PAREN)
4147
4148        if self.TABLESAMPLE_CSV:
4149            num = None
4150            expressions = self._parse_csv(self._parse_primary)
4151        else:
4152            expressions = None
4153            num = (
4154                self._parse_factor()
4155                if self._match(TokenType.NUMBER, advance=False)
4156                else self._parse_primary() or self._parse_placeholder()
4157            )
4158
4159        if self._match_text_seq("BUCKET"):
4160            bucket_numerator = self._parse_number()
4161            self._match_text_seq("OUT", "OF")
4162            bucket_denominator = bucket_denominator = self._parse_number()
4163            self._match(TokenType.ON)
4164            bucket_field = self._parse_field()
4165        elif self._match_set((TokenType.PERCENT, TokenType.MOD)):
4166            percent = num
4167        elif self._match(TokenType.ROWS) or not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
4168            size = num
4169        else:
4170            percent = num
4171
4172        if matched_l_paren:
4173            self._match_r_paren()
4174
4175        if self._match(TokenType.L_PAREN):
4176            method = self._parse_var(upper=True)
4177            seed = self._match(TokenType.COMMA) and self._parse_number()
4178            self._match_r_paren()
4179        elif self._match_texts(("SEED", "REPEATABLE")):
4180            seed = self._parse_wrapped(self._parse_number)
4181
4182        if not method and self.DEFAULT_SAMPLING_METHOD:
4183            method = exp.var(self.DEFAULT_SAMPLING_METHOD)
4184
4185        return self.expression(
4186            exp.TableSample,
4187            expressions=expressions,
4188            method=method,
4189            bucket_numerator=bucket_numerator,
4190            bucket_denominator=bucket_denominator,
4191            bucket_field=bucket_field,
4192            percent=percent,
4193            size=size,
4194            seed=seed,
4195        )
4196
4197    def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]:
4198        return list(iter(self._parse_pivot, None)) or None
4199
4200    def _parse_joins(self) -> t.Iterator[exp.Join]:
4201        return iter(self._parse_join, None)
4202
4203    def _parse_unpivot_columns(self) -> t.Optional[exp.UnpivotColumns]:
4204        if not self._match(TokenType.INTO):
4205            return None
4206
4207        return self.expression(
4208            exp.UnpivotColumns,
4209            this=self._match_text_seq("NAME") and self._parse_column(),
4210            expressions=self._match_text_seq("VALUE") and self._parse_csv(self._parse_column),
4211        )
4212
4213    # https://duckdb.org/docs/sql/statements/pivot
4214    def _parse_simplified_pivot(self, is_unpivot: t.Optional[bool] = None) -> exp.Pivot:
4215        def _parse_on() -> t.Optional[exp.Expression]:
4216            this = self._parse_bitwise()
4217
4218            if self._match(TokenType.IN):
4219                # PIVOT ... ON col IN (row_val1, row_val2)
4220                return self._parse_in(this)
4221            if self._match(TokenType.ALIAS, advance=False):
4222                # UNPIVOT ... ON (col1, col2, col3) AS row_val
4223                return self._parse_alias(this)
4224
4225            return this
4226
4227        this = self._parse_table()
4228        expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on)
4229        into = self._parse_unpivot_columns()
4230        using = self._match(TokenType.USING) and self._parse_csv(
4231            lambda: self._parse_alias(self._parse_function())
4232        )
4233        group = self._parse_group()
4234
4235        return self.expression(
4236            exp.Pivot,
4237            this=this,
4238            expressions=expressions,
4239            using=using,
4240            group=group,
4241            unpivot=is_unpivot,
4242            into=into,
4243        )
4244
4245    def _parse_pivot_in(self) -> exp.In:
4246        def _parse_aliased_expression() -> t.Optional[exp.Expression]:
4247            this = self._parse_select_or_expression()
4248
4249            self._match(TokenType.ALIAS)
4250            alias = self._parse_bitwise()
4251            if alias:
4252                if isinstance(alias, exp.Column) and not alias.db:
4253                    alias = alias.this
4254                return self.expression(exp.PivotAlias, this=this, alias=alias)
4255
4256            return this
4257
4258        value = self._parse_column()
4259
4260        if not self._match_pair(TokenType.IN, TokenType.L_PAREN):
4261            self.raise_error("Expecting IN (")
4262
4263        if self._match(TokenType.ANY):
4264            exprs: t.List[exp.Expression] = ensure_list(exp.PivotAny(this=self._parse_order()))
4265        else:
4266            exprs = self._parse_csv(_parse_aliased_expression)
4267
4268        self._match_r_paren()
4269        return self.expression(exp.In, this=value, expressions=exprs)
4270
4271    def _parse_pivot(self) -> t.Optional[exp.Pivot]:
4272        index = self._index
4273        include_nulls = None
4274
4275        if self._match(TokenType.PIVOT):
4276            unpivot = False
4277        elif self._match(TokenType.UNPIVOT):
4278            unpivot = True
4279
4280            # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax
4281            if self._match_text_seq("INCLUDE", "NULLS"):
4282                include_nulls = True
4283            elif self._match_text_seq("EXCLUDE", "NULLS"):
4284                include_nulls = False
4285        else:
4286            return None
4287
4288        expressions = []
4289
4290        if not self._match(TokenType.L_PAREN):
4291            self._retreat(index)
4292            return None
4293
4294        if unpivot:
4295            expressions = self._parse_csv(self._parse_column)
4296        else:
4297            expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function()))
4298
4299        if not expressions:
4300            self.raise_error("Failed to parse PIVOT's aggregation list")
4301
4302        if not self._match(TokenType.FOR):
4303            self.raise_error("Expecting FOR")
4304
4305        fields = []
4306        while True:
4307            field = self._try_parse(self._parse_pivot_in)
4308            if not field:
4309                break
4310            fields.append(field)
4311
4312        default_on_null = self._match_text_seq("DEFAULT", "ON", "NULL") and self._parse_wrapped(
4313            self._parse_bitwise
4314        )
4315
4316        group = self._parse_group()
4317
4318        self._match_r_paren()
4319
4320        pivot = self.expression(
4321            exp.Pivot,
4322            expressions=expressions,
4323            fields=fields,
4324            unpivot=unpivot,
4325            include_nulls=include_nulls,
4326            default_on_null=default_on_null,
4327            group=group,
4328        )
4329
4330        if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False):
4331            pivot.set("alias", self._parse_table_alias())
4332
4333        if not unpivot:
4334            names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions))
4335
4336            columns: t.List[exp.Expression] = []
4337            all_fields = []
4338            for pivot_field in pivot.fields:
4339                pivot_field_expressions = pivot_field.expressions
4340
4341                # The `PivotAny` expression corresponds to `ANY ORDER BY <column>`; we can't infer in this case.
4342                if isinstance(seq_get(pivot_field_expressions, 0), exp.PivotAny):
4343                    continue
4344
4345                all_fields.append(
4346                    [
4347                        fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name
4348                        for fld in pivot_field_expressions
4349                    ]
4350                )
4351
4352            if all_fields:
4353                if names:
4354                    all_fields.append(names)
4355
4356                # Generate all possible combinations of the pivot columns
4357                # e.g PIVOT(sum(...) as total FOR year IN (2000, 2010) FOR country IN ('NL', 'US'))
4358                # generates the product between [[2000, 2010], ['NL', 'US'], ['total']]
4359                for fld_parts_tuple in itertools.product(*all_fields):
4360                    fld_parts = list(fld_parts_tuple)
4361
4362                    if names and self.PREFIXED_PIVOT_COLUMNS:
4363                        # Move the "name" to the front of the list
4364                        fld_parts.insert(0, fld_parts.pop(-1))
4365
4366                    columns.append(exp.to_identifier("_".join(fld_parts)))
4367
4368            pivot.set("columns", columns)
4369
4370        return pivot
4371
4372    def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]:
4373        return [agg.alias for agg in aggregations if agg.alias]
4374
4375    def _parse_prewhere(self, skip_where_token: bool = False) -> t.Optional[exp.PreWhere]:
4376        if not skip_where_token and not self._match(TokenType.PREWHERE):
4377            return None
4378
4379        return self.expression(
4380            exp.PreWhere, comments=self._prev_comments, this=self._parse_assignment()
4381        )
4382
4383    def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]:
4384        if not skip_where_token and not self._match(TokenType.WHERE):
4385            return None
4386
4387        return self.expression(
4388            exp.Where, comments=self._prev_comments, this=self._parse_assignment()
4389        )
4390
4391    def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]:
4392        if not skip_group_by_token and not self._match(TokenType.GROUP_BY):
4393            return None
4394
4395        elements: t.Dict[str, t.Any] = defaultdict(list)
4396
4397        if self._match(TokenType.ALL):
4398            elements["all"] = True
4399        elif self._match(TokenType.DISTINCT):
4400            elements["all"] = False
4401
4402        while True:
4403            index = self._index
4404
4405            elements["expressions"].extend(
4406                self._parse_csv(
4407                    lambda: None
4408                    if self._match_set((TokenType.CUBE, TokenType.ROLLUP), advance=False)
4409                    else self._parse_assignment()
4410                )
4411            )
4412
4413            before_with_index = self._index
4414            with_prefix = self._match(TokenType.WITH)
4415
4416            if self._match(TokenType.ROLLUP):
4417                elements["rollup"].append(
4418                    self._parse_cube_or_rollup(exp.Rollup, with_prefix=with_prefix)
4419                )
4420            elif self._match(TokenType.CUBE):
4421                elements["cube"].append(
4422                    self._parse_cube_or_rollup(exp.Cube, with_prefix=with_prefix)
4423                )
4424            elif self._match(TokenType.GROUPING_SETS):
4425                elements["grouping_sets"].append(
4426                    self.expression(
4427                        exp.GroupingSets,
4428                        expressions=self._parse_wrapped_csv(self._parse_grouping_set),
4429                    )
4430                )
4431            elif self._match_text_seq("TOTALS"):
4432                elements["totals"] = True  # type: ignore
4433
4434            if before_with_index <= self._index <= before_with_index + 1:
4435                self._retreat(before_with_index)
4436                break
4437
4438            if index == self._index:
4439                break
4440
4441        return self.expression(exp.Group, **elements)  # type: ignore
4442
4443    def _parse_cube_or_rollup(self, kind: t.Type[E], with_prefix: bool = False) -> E:
4444        return self.expression(
4445            kind, expressions=[] if with_prefix else self._parse_wrapped_csv(self._parse_column)
4446        )
4447
4448    def _parse_grouping_set(self) -> t.Optional[exp.Expression]:
4449        if self._match(TokenType.L_PAREN):
4450            grouping_set = self._parse_csv(self._parse_column)
4451            self._match_r_paren()
4452            return self.expression(exp.Tuple, expressions=grouping_set)
4453
4454        return self._parse_column()
4455
4456    def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]:
4457        if not skip_having_token and not self._match(TokenType.HAVING):
4458            return None
4459        return self.expression(exp.Having, this=self._parse_assignment())
4460
4461    def _parse_qualify(self) -> t.Optional[exp.Qualify]:
4462        if not self._match(TokenType.QUALIFY):
4463            return None
4464        return self.expression(exp.Qualify, this=self._parse_assignment())
4465
4466    def _parse_connect_with_prior(self) -> t.Optional[exp.Expression]:
4467        self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression(
4468            exp.Prior, this=self._parse_bitwise()
4469        )
4470        connect = self._parse_assignment()
4471        self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR")
4472        return connect
4473
4474    def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]:
4475        if skip_start_token:
4476            start = None
4477        elif self._match(TokenType.START_WITH):
4478            start = self._parse_assignment()
4479        else:
4480            return None
4481
4482        self._match(TokenType.CONNECT_BY)
4483        nocycle = self._match_text_seq("NOCYCLE")
4484        connect = self._parse_connect_with_prior()
4485
4486        if not start and self._match(TokenType.START_WITH):
4487            start = self._parse_assignment()
4488
4489        return self.expression(exp.Connect, start=start, connect=connect, nocycle=nocycle)
4490
4491    def _parse_name_as_expression(self) -> t.Optional[exp.Expression]:
4492        this = self._parse_id_var(any_token=True)
4493        if self._match(TokenType.ALIAS):
4494            this = self.expression(exp.Alias, alias=this, this=self._parse_assignment())
4495        return this
4496
4497    def _parse_interpolate(self) -> t.Optional[t.List[exp.Expression]]:
4498        if self._match_text_seq("INTERPOLATE"):
4499            return self._parse_wrapped_csv(self._parse_name_as_expression)
4500        return None
4501
4502    def _parse_order(
4503        self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False
4504    ) -> t.Optional[exp.Expression]:
4505        siblings = None
4506        if not skip_order_token and not self._match(TokenType.ORDER_BY):
4507            if not self._match(TokenType.ORDER_SIBLINGS_BY):
4508                return this
4509
4510            siblings = True
4511
4512        return self.expression(
4513            exp.Order,
4514            this=this,
4515            expressions=self._parse_csv(self._parse_ordered),
4516            siblings=siblings,
4517        )
4518
4519    def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]:
4520        if not self._match(token):
4521            return None
4522        return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered))
4523
4524    def _parse_ordered(
4525        self, parse_method: t.Optional[t.Callable] = None
4526    ) -> t.Optional[exp.Ordered]:
4527        this = parse_method() if parse_method else self._parse_assignment()
4528        if not this:
4529            return None
4530
4531        if this.name.upper() == "ALL" and self.dialect.SUPPORTS_ORDER_BY_ALL:
4532            this = exp.var("ALL")
4533
4534        asc = self._match(TokenType.ASC)
4535        desc = self._match(TokenType.DESC) or (asc and False)
4536
4537        is_nulls_first = self._match_text_seq("NULLS", "FIRST")
4538        is_nulls_last = self._match_text_seq("NULLS", "LAST")
4539
4540        nulls_first = is_nulls_first or False
4541        explicitly_null_ordered = is_nulls_first or is_nulls_last
4542
4543        if (
4544            not explicitly_null_ordered
4545            and (
4546                (not desc and self.dialect.NULL_ORDERING == "nulls_are_small")
4547                or (desc and self.dialect.NULL_ORDERING != "nulls_are_small")
4548            )
4549            and self.dialect.NULL_ORDERING != "nulls_are_last"
4550        ):
4551            nulls_first = True
4552
4553        if self._match_text_seq("WITH", "FILL"):
4554            with_fill = self.expression(
4555                exp.WithFill,
4556                **{  # type: ignore
4557                    "from": self._match(TokenType.FROM) and self._parse_bitwise(),
4558                    "to": self._match_text_seq("TO") and self._parse_bitwise(),
4559                    "step": self._match_text_seq("STEP") and self._parse_bitwise(),
4560                    "interpolate": self._parse_interpolate(),
4561                },
4562            )
4563        else:
4564            with_fill = None
4565
4566        return self.expression(
4567            exp.Ordered, this=this, desc=desc, nulls_first=nulls_first, with_fill=with_fill
4568        )
4569
4570    def _parse_limit_options(self) -> exp.LimitOptions:
4571        percent = self._match(TokenType.PERCENT)
4572        rows = self._match_set((TokenType.ROW, TokenType.ROWS))
4573        self._match_text_seq("ONLY")
4574        with_ties = self._match_text_seq("WITH", "TIES")
4575        return self.expression(exp.LimitOptions, percent=percent, rows=rows, with_ties=with_ties)
4576
4577    def _parse_limit(
4578        self,
4579        this: t.Optional[exp.Expression] = None,
4580        top: bool = False,
4581        skip_limit_token: bool = False,
4582    ) -> t.Optional[exp.Expression]:
4583        if skip_limit_token or self._match(TokenType.TOP if top else TokenType.LIMIT):
4584            comments = self._prev_comments
4585            if top:
4586                limit_paren = self._match(TokenType.L_PAREN)
4587                expression = self._parse_term() if limit_paren else self._parse_number()
4588
4589                if limit_paren:
4590                    self._match_r_paren()
4591
4592                limit_options = self._parse_limit_options()
4593            else:
4594                limit_options = None
4595                expression = self._parse_term()
4596
4597            if self._match(TokenType.COMMA):
4598                offset = expression
4599                expression = self._parse_term()
4600            else:
4601                offset = None
4602
4603            limit_exp = self.expression(
4604                exp.Limit,
4605                this=this,
4606                expression=expression,
4607                offset=offset,
4608                comments=comments,
4609                limit_options=limit_options,
4610                expressions=self._parse_limit_by(),
4611            )
4612
4613            return limit_exp
4614
4615        if self._match(TokenType.FETCH):
4616            direction = self._match_set((TokenType.FIRST, TokenType.NEXT))
4617            direction = self._prev.text.upper() if direction else "FIRST"
4618
4619            count = self._parse_field(tokens=self.FETCH_TOKENS)
4620
4621            return self.expression(
4622                exp.Fetch,
4623                direction=direction,
4624                count=count,
4625                limit_options=self._parse_limit_options(),
4626            )
4627
4628        return this
4629
4630    def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4631        if not self._match(TokenType.OFFSET):
4632            return this
4633
4634        count = self._parse_term()
4635        self._match_set((TokenType.ROW, TokenType.ROWS))
4636
4637        return self.expression(
4638            exp.Offset, this=this, expression=count, expressions=self._parse_limit_by()
4639        )
4640
4641    def _can_parse_limit_or_offset(self) -> bool:
4642        if not self._match_set(self.AMBIGUOUS_ALIAS_TOKENS, advance=False):
4643            return False
4644
4645        index = self._index
4646        result = bool(
4647            self._try_parse(self._parse_limit, retreat=True)
4648            or self._try_parse(self._parse_offset, retreat=True)
4649        )
4650        self._retreat(index)
4651        return result
4652
4653    def _parse_limit_by(self) -> t.Optional[t.List[exp.Expression]]:
4654        return self._match_text_seq("BY") and self._parse_csv(self._parse_bitwise)
4655
4656    def _parse_locks(self) -> t.List[exp.Lock]:
4657        locks = []
4658        while True:
4659            if self._match_text_seq("FOR", "UPDATE"):
4660                update = True
4661            elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq(
4662                "LOCK", "IN", "SHARE", "MODE"
4663            ):
4664                update = False
4665            else:
4666                break
4667
4668            expressions = None
4669            if self._match_text_seq("OF"):
4670                expressions = self._parse_csv(lambda: self._parse_table(schema=True))
4671
4672            wait: t.Optional[bool | exp.Expression] = None
4673            if self._match_text_seq("NOWAIT"):
4674                wait = True
4675            elif self._match_text_seq("WAIT"):
4676                wait = self._parse_primary()
4677            elif self._match_text_seq("SKIP", "LOCKED"):
4678                wait = False
4679
4680            locks.append(
4681                self.expression(exp.Lock, update=update, expressions=expressions, wait=wait)
4682            )
4683
4684        return locks
4685
4686    def parse_set_operation(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4687        start = self._index
4688        _, side_token, kind_token = self._parse_join_parts()
4689
4690        side = side_token.text if side_token else None
4691        kind = kind_token.text if kind_token else None
4692
4693        if not self._match_set(self.SET_OPERATIONS):
4694            self._retreat(start)
4695            return None
4696
4697        token_type = self._prev.token_type
4698
4699        if token_type == TokenType.UNION:
4700            operation: t.Type[exp.SetOperation] = exp.Union
4701        elif token_type == TokenType.EXCEPT:
4702            operation = exp.Except
4703        else:
4704            operation = exp.Intersect
4705
4706        comments = self._prev.comments
4707
4708        if self._match(TokenType.DISTINCT):
4709            distinct: t.Optional[bool] = True
4710        elif self._match(TokenType.ALL):
4711            distinct = False
4712        else:
4713            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4714            if distinct is None:
4715                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4716
4717        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4718            "STRICT", "CORRESPONDING"
4719        )
4720        if self._match_text_seq("CORRESPONDING"):
4721            by_name = True
4722            if not side and not kind:
4723                kind = "INNER"
4724
4725        on_column_list = None
4726        if by_name and self._match_texts(("ON", "BY")):
4727            on_column_list = self._parse_wrapped_csv(self._parse_column)
4728
4729        expression = self._parse_select(nested=True, parse_set_operation=False)
4730
4731        return self.expression(
4732            operation,
4733            comments=comments,
4734            this=this,
4735            distinct=distinct,
4736            by_name=by_name,
4737            expression=expression,
4738            side=side,
4739            kind=kind,
4740            on=on_column_list,
4741        )
4742
4743    def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4744        while this:
4745            setop = self.parse_set_operation(this)
4746            if not setop:
4747                break
4748            this = setop
4749
4750        if isinstance(this, exp.SetOperation) and self.MODIFIERS_ATTACHED_TO_SET_OP:
4751            expression = this.expression
4752
4753            if expression:
4754                for arg in self.SET_OP_MODIFIERS:
4755                    expr = expression.args.get(arg)
4756                    if expr:
4757                        this.set(arg, expr.pop())
4758
4759        return this
4760
4761    def _parse_expression(self) -> t.Optional[exp.Expression]:
4762        return self._parse_alias(self._parse_assignment())
4763
4764    def _parse_assignment(self) -> t.Optional[exp.Expression]:
4765        this = self._parse_disjunction()
4766        if not this and self._next and self._next.token_type in self.ASSIGNMENT:
4767            # This allows us to parse <non-identifier token> := <expr>
4768            this = exp.column(
4769                t.cast(str, self._advance_any(ignore_reserved=True) and self._prev.text)
4770            )
4771
4772        while self._match_set(self.ASSIGNMENT):
4773            if isinstance(this, exp.Column) and len(this.parts) == 1:
4774                this = this.this
4775
4776            this = self.expression(
4777                self.ASSIGNMENT[self._prev.token_type],
4778                this=this,
4779                comments=self._prev_comments,
4780                expression=self._parse_assignment(),
4781            )
4782
4783        return this
4784
4785    def _parse_disjunction(self) -> t.Optional[exp.Expression]:
4786        return self._parse_tokens(self._parse_conjunction, self.DISJUNCTION)
4787
4788    def _parse_conjunction(self) -> t.Optional[exp.Expression]:
4789        return self._parse_tokens(self._parse_equality, self.CONJUNCTION)
4790
4791    def _parse_equality(self) -> t.Optional[exp.Expression]:
4792        return self._parse_tokens(self._parse_comparison, self.EQUALITY)
4793
4794    def _parse_comparison(self) -> t.Optional[exp.Expression]:
4795        return self._parse_tokens(self._parse_range, self.COMPARISON)
4796
4797    def _parse_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4798        this = this or self._parse_bitwise()
4799        negate = self._match(TokenType.NOT)
4800
4801        if self._match_set(self.RANGE_PARSERS):
4802            expression = self.RANGE_PARSERS[self._prev.token_type](self, this)
4803            if not expression:
4804                return this
4805
4806            this = expression
4807        elif self._match(TokenType.ISNULL):
4808            this = self.expression(exp.Is, this=this, expression=exp.Null())
4809
4810        # Postgres supports ISNULL and NOTNULL for conditions.
4811        # https://blog.andreiavram.ro/postgresql-null-composite-type/
4812        if self._match(TokenType.NOTNULL):
4813            this = self.expression(exp.Is, this=this, expression=exp.Null())
4814            this = self.expression(exp.Not, this=this)
4815
4816        if negate:
4817            this = self._negate_range(this)
4818
4819        if self._match(TokenType.IS):
4820            this = self._parse_is(this)
4821
4822        return this
4823
4824    def _negate_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4825        if not this:
4826            return this
4827
4828        return self.expression(exp.Not, this=this)
4829
4830    def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4831        index = self._index - 1
4832        negate = self._match(TokenType.NOT)
4833
4834        if self._match_text_seq("DISTINCT", "FROM"):
4835            klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ
4836            return self.expression(klass, this=this, expression=self._parse_bitwise())
4837
4838        if self._match(TokenType.JSON):
4839            kind = self._match_texts(self.IS_JSON_PREDICATE_KIND) and self._prev.text.upper()
4840
4841            if self._match_text_seq("WITH"):
4842                _with = True
4843            elif self._match_text_seq("WITHOUT"):
4844                _with = False
4845            else:
4846                _with = None
4847
4848            unique = self._match(TokenType.UNIQUE)
4849            self._match_text_seq("KEYS")
4850            expression: t.Optional[exp.Expression] = self.expression(
4851                exp.JSON, **{"this": kind, "with": _with, "unique": unique}
4852            )
4853        else:
4854            expression = self._parse_primary() or self._parse_null()
4855            if not expression:
4856                self._retreat(index)
4857                return None
4858
4859        this = self.expression(exp.Is, this=this, expression=expression)
4860        return self.expression(exp.Not, this=this) if negate else this
4861
4862    def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In:
4863        unnest = self._parse_unnest(with_alias=False)
4864        if unnest:
4865            this = self.expression(exp.In, this=this, unnest=unnest)
4866        elif self._match_set((TokenType.L_PAREN, TokenType.L_BRACKET)):
4867            matched_l_paren = self._prev.token_type == TokenType.L_PAREN
4868            expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias))
4869
4870            if len(expressions) == 1 and isinstance(expressions[0], exp.Query):
4871                this = self.expression(exp.In, this=this, query=expressions[0].subquery(copy=False))
4872            else:
4873                this = self.expression(exp.In, this=this, expressions=expressions)
4874
4875            if matched_l_paren:
4876                self._match_r_paren(this)
4877            elif not self._match(TokenType.R_BRACKET, expression=this):
4878                self.raise_error("Expecting ]")
4879        else:
4880            this = self.expression(exp.In, this=this, field=self._parse_column())
4881
4882        return this
4883
4884    def _parse_between(self, this: t.Optional[exp.Expression]) -> exp.Between:
4885        low = self._parse_bitwise()
4886        self._match(TokenType.AND)
4887        high = self._parse_bitwise()
4888        return self.expression(exp.Between, this=this, low=low, high=high)
4889
4890    def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4891        if not self._match(TokenType.ESCAPE):
4892            return this
4893        return self.expression(exp.Escape, this=this, expression=self._parse_string())
4894
4895    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Add | exp.Interval]:
4896        index = self._index
4897
4898        if not self._match(TokenType.INTERVAL) and match_interval:
4899            return None
4900
4901        if self._match(TokenType.STRING, advance=False):
4902            this = self._parse_primary()
4903        else:
4904            this = self._parse_term()
4905
4906        if not this or (
4907            isinstance(this, exp.Column)
4908            and not this.table
4909            and not this.this.quoted
4910            and this.name.upper() == "IS"
4911        ):
4912            self._retreat(index)
4913            return None
4914
4915        unit = self._parse_function() or (
4916            not self._match(TokenType.ALIAS, advance=False)
4917            and self._parse_var(any_token=True, upper=True)
4918        )
4919
4920        # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse
4921        # each INTERVAL expression into this canonical form so it's easy to transpile
4922        if this and this.is_number:
4923            this = exp.Literal.string(this.to_py())
4924        elif this and this.is_string:
4925            parts = exp.INTERVAL_STRING_RE.findall(this.name)
4926            if parts and unit:
4927                # Unconsume the eagerly-parsed unit, since the real unit was part of the string
4928                unit = None
4929                self._retreat(self._index - 1)
4930
4931            if len(parts) == 1:
4932                this = exp.Literal.string(parts[0][0])
4933                unit = self.expression(exp.Var, this=parts[0][1].upper())
4934        if self.INTERVAL_SPANS and self._match_text_seq("TO"):
4935            unit = self.expression(
4936                exp.IntervalSpan, this=unit, expression=self._parse_var(any_token=True, upper=True)
4937            )
4938
4939        interval = self.expression(exp.Interval, this=this, unit=unit)
4940
4941        index = self._index
4942        self._match(TokenType.PLUS)
4943
4944        # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
4945        if self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
4946            return self.expression(
4947                exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
4948            )
4949
4950        self._retreat(index)
4951        return interval
4952
4953    def _parse_bitwise(self) -> t.Optional[exp.Expression]:
4954        this = self._parse_term()
4955
4956        while True:
4957            if self._match_set(self.BITWISE):
4958                this = self.expression(
4959                    self.BITWISE[self._prev.token_type],
4960                    this=this,
4961                    expression=self._parse_term(),
4962                )
4963            elif self.dialect.DPIPE_IS_STRING_CONCAT and self._match(TokenType.DPIPE):
4964                this = self.expression(
4965                    exp.DPipe,
4966                    this=this,
4967                    expression=self._parse_term(),
4968                    safe=not self.dialect.STRICT_STRING_CONCAT,
4969                )
4970            elif self._match(TokenType.DQMARK):
4971                this = self.expression(
4972                    exp.Coalesce, this=this, expressions=ensure_list(self._parse_term())
4973                )
4974            elif self._match_pair(TokenType.LT, TokenType.LT):
4975                this = self.expression(
4976                    exp.BitwiseLeftShift, this=this, expression=self._parse_term()
4977                )
4978            elif self._match_pair(TokenType.GT, TokenType.GT):
4979                this = self.expression(
4980                    exp.BitwiseRightShift, this=this, expression=self._parse_term()
4981                )
4982            else:
4983                break
4984
4985        return this
4986
4987    def _parse_term(self) -> t.Optional[exp.Expression]:
4988        this = self._parse_factor()
4989
4990        while self._match_set(self.TERM):
4991            klass = self.TERM[self._prev.token_type]
4992            comments = self._prev_comments
4993            expression = self._parse_factor()
4994
4995            this = self.expression(klass, this=this, comments=comments, expression=expression)
4996
4997            if isinstance(this, exp.Collate):
4998                expr = this.expression
4999
5000                # Preserve collations such as pg_catalog."default" (Postgres) as columns, otherwise
5001                # fallback to Identifier / Var
5002                if isinstance(expr, exp.Column) and len(expr.parts) == 1:
5003                    ident = expr.this
5004                    if isinstance(ident, exp.Identifier):
5005                        this.set("expression", ident if ident.quoted else exp.var(ident.name))
5006
5007        return this
5008
5009    def _parse_factor(self) -> t.Optional[exp.Expression]:
5010        parse_method = self._parse_exponent if self.EXPONENT else self._parse_unary
5011        this = parse_method()
5012
5013        while self._match_set(self.FACTOR):
5014            klass = self.FACTOR[self._prev.token_type]
5015            comments = self._prev_comments
5016            expression = parse_method()
5017
5018            if not expression and klass is exp.IntDiv and self._prev.text.isalpha():
5019                self._retreat(self._index - 1)
5020                return this
5021
5022            this = self.expression(klass, this=this, comments=comments, expression=expression)
5023
5024            if isinstance(this, exp.Div):
5025                this.args["typed"] = self.dialect.TYPED_DIVISION
5026                this.args["safe"] = self.dialect.SAFE_DIVISION
5027
5028        return this
5029
5030    def _parse_exponent(self) -> t.Optional[exp.Expression]:
5031        return self._parse_tokens(self._parse_unary, self.EXPONENT)
5032
5033    def _parse_unary(self) -> t.Optional[exp.Expression]:
5034        if self._match_set(self.UNARY_PARSERS):
5035            return self.UNARY_PARSERS[self._prev.token_type](self)
5036        return self._parse_at_time_zone(self._parse_type())
5037
5038    def _parse_type(
5039        self, parse_interval: bool = True, fallback_to_identifier: bool = False
5040    ) -> t.Optional[exp.Expression]:
5041        interval = parse_interval and self._parse_interval()
5042        if interval:
5043            return interval
5044
5045        index = self._index
5046        data_type = self._parse_types(check_func=True, allow_identifiers=False)
5047
5048        # parse_types() returns a Cast if we parsed BQ's inline constructor <type>(<values>) e.g.
5049        # STRUCT<a INT, b STRING>(1, 'foo'), which is canonicalized to CAST(<values> AS <type>)
5050        if isinstance(data_type, exp.Cast):
5051            # This constructor can contain ops directly after it, for instance struct unnesting:
5052            # STRUCT<a INT, b STRING>(1, 'foo').* --> CAST(STRUCT(1, 'foo') AS STRUCT<a iNT, b STRING).*
5053            return self._parse_column_ops(data_type)
5054
5055        if data_type:
5056            index2 = self._index
5057            this = self._parse_primary()
5058
5059            if isinstance(this, exp.Literal):
5060                this = self._parse_column_ops(this)
5061
5062                parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
5063                if parser:
5064                    return parser(self, this, data_type)
5065
5066                return self.expression(exp.Cast, this=this, to=data_type)
5067
5068            # The expressions arg gets set by the parser when we have something like DECIMAL(38, 0)
5069            # in the input SQL. In that case, we'll produce these tokens: DECIMAL ( 38 , 0 )
5070            #
5071            # If the index difference here is greater than 1, that means the parser itself must have
5072            # consumed additional tokens such as the DECIMAL scale and precision in the above example.
5073            #
5074            # If it's not greater than 1, then it must be 1, because we've consumed at least the type
5075            # keyword, meaning that the expressions arg of the DataType must have gotten set by a
5076            # callable in the TYPE_CONVERTERS mapping. For example, Snowflake converts DECIMAL to
5077            # DECIMAL(38, 0)) in order to facilitate the data type's transpilation.
5078            #
5079            # In these cases, we don't really want to return the converted type, but instead retreat
5080            # and try to parse a Column or Identifier in the section below.
5081            if data_type.expressions and index2 - index > 1:
5082                self._retreat(index2)
5083                return self._parse_column_ops(data_type)
5084
5085            self._retreat(index)
5086
5087        if fallback_to_identifier:
5088            return self._parse_id_var()
5089
5090        this = self._parse_column()
5091        return this and self._parse_column_ops(this)
5092
5093    def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]:
5094        this = self._parse_type()
5095        if not this:
5096            return None
5097
5098        if isinstance(this, exp.Column) and not this.table:
5099            this = exp.var(this.name.upper())
5100
5101        return self.expression(
5102            exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True)
5103        )
5104
5105    def _parse_types(
5106        self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
5107    ) -> t.Optional[exp.Expression]:
5108        index = self._index
5109
5110        this: t.Optional[exp.Expression] = None
5111        prefix = self._match_text_seq("SYSUDTLIB", ".")
5112
5113        if not self._match_set(self.TYPE_TOKENS):
5114            identifier = allow_identifiers and self._parse_id_var(
5115                any_token=False, tokens=(TokenType.VAR,)
5116            )
5117            if isinstance(identifier, exp.Identifier):
5118                tokens = self.dialect.tokenize(identifier.sql(dialect=self.dialect))
5119
5120                if len(tokens) != 1:
5121                    self.raise_error("Unexpected identifier", self._prev)
5122
5123                if tokens[0].token_type in self.TYPE_TOKENS:
5124                    self._prev = tokens[0]
5125                elif self.dialect.SUPPORTS_USER_DEFINED_TYPES:
5126                    type_name = identifier.name
5127
5128                    while self._match(TokenType.DOT):
5129                        type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
5130
5131                    this = exp.DataType.build(type_name, udt=True)
5132                else:
5133                    self._retreat(self._index - 1)
5134                    return None
5135            else:
5136                return None
5137
5138        type_token = self._prev.token_type
5139
5140        if type_token == TokenType.PSEUDO_TYPE:
5141            return self.expression(exp.PseudoType, this=self._prev.text.upper())
5142
5143        if type_token == TokenType.OBJECT_IDENTIFIER:
5144            return self.expression(exp.ObjectIdentifier, this=self._prev.text.upper())
5145
5146        # https://materialize.com/docs/sql/types/map/
5147        if type_token == TokenType.MAP and self._match(TokenType.L_BRACKET):
5148            key_type = self._parse_types(
5149                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5150            )
5151            if not self._match(TokenType.FARROW):
5152                self._retreat(index)
5153                return None
5154
5155            value_type = self._parse_types(
5156                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5157            )
5158            if not self._match(TokenType.R_BRACKET):
5159                self._retreat(index)
5160                return None
5161
5162            return exp.DataType(
5163                this=exp.DataType.Type.MAP,
5164                expressions=[key_type, value_type],
5165                nested=True,
5166                prefix=prefix,
5167            )
5168
5169        nested = type_token in self.NESTED_TYPE_TOKENS
5170        is_struct = type_token in self.STRUCT_TYPE_TOKENS
5171        is_aggregate = type_token in self.AGGREGATE_TYPE_TOKENS
5172        expressions = None
5173        maybe_func = False
5174
5175        if self._match(TokenType.L_PAREN):
5176            if is_struct:
5177                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5178            elif nested:
5179                expressions = self._parse_csv(
5180                    lambda: self._parse_types(
5181                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5182                    )
5183                )
5184                if type_token == TokenType.NULLABLE and len(expressions) == 1:
5185                    this = expressions[0]
5186                    this.set("nullable", True)
5187                    self._match_r_paren()
5188                    return this
5189            elif type_token in self.ENUM_TYPE_TOKENS:
5190                expressions = self._parse_csv(self._parse_equality)
5191            elif is_aggregate:
5192                func_or_ident = self._parse_function(anonymous=True) or self._parse_id_var(
5193                    any_token=False, tokens=(TokenType.VAR, TokenType.ANY)
5194                )
5195                if not func_or_ident:
5196                    return None
5197                expressions = [func_or_ident]
5198                if self._match(TokenType.COMMA):
5199                    expressions.extend(
5200                        self._parse_csv(
5201                            lambda: self._parse_types(
5202                                check_func=check_func,
5203                                schema=schema,
5204                                allow_identifiers=allow_identifiers,
5205                            )
5206                        )
5207                    )
5208            else:
5209                expressions = self._parse_csv(self._parse_type_size)
5210
5211                # https://docs.snowflake.com/en/sql-reference/data-types-vector
5212                if type_token == TokenType.VECTOR and len(expressions) == 2:
5213                    expressions[0] = exp.DataType.build(expressions[0].name, dialect=self.dialect)
5214
5215            if not expressions or not self._match(TokenType.R_PAREN):
5216                self._retreat(index)
5217                return None
5218
5219            maybe_func = True
5220
5221        values: t.Optional[t.List[exp.Expression]] = None
5222
5223        if nested and self._match(TokenType.LT):
5224            if is_struct:
5225                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5226            else:
5227                expressions = self._parse_csv(
5228                    lambda: self._parse_types(
5229                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5230                    )
5231                )
5232
5233            if not self._match(TokenType.GT):
5234                self.raise_error("Expecting >")
5235
5236            if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)):
5237                values = self._parse_csv(self._parse_assignment)
5238                if not values and is_struct:
5239                    values = None
5240                    self._retreat(self._index - 1)
5241                else:
5242                    self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN))
5243
5244        if type_token in self.TIMESTAMPS:
5245            if self._match_text_seq("WITH", "TIME", "ZONE"):
5246                maybe_func = False
5247                tz_type = (
5248                    exp.DataType.Type.TIMETZ
5249                    if type_token in self.TIMES
5250                    else exp.DataType.Type.TIMESTAMPTZ
5251                )
5252                this = exp.DataType(this=tz_type, expressions=expressions)
5253            elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"):
5254                maybe_func = False
5255                this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions)
5256            elif self._match_text_seq("WITHOUT", "TIME", "ZONE"):
5257                maybe_func = False
5258        elif type_token == TokenType.INTERVAL:
5259            unit = self._parse_var(upper=True)
5260            if unit:
5261                if self._match_text_seq("TO"):
5262                    unit = exp.IntervalSpan(this=unit, expression=self._parse_var(upper=True))
5263
5264                this = self.expression(exp.DataType, this=self.expression(exp.Interval, unit=unit))
5265            else:
5266                this = self.expression(exp.DataType, this=exp.DataType.Type.INTERVAL)
5267        elif type_token == TokenType.VOID:
5268            this = exp.DataType(this=exp.DataType.Type.NULL)
5269
5270        if maybe_func and check_func:
5271            index2 = self._index
5272            peek = self._parse_string()
5273
5274            if not peek:
5275                self._retreat(index)
5276                return None
5277
5278            self._retreat(index2)
5279
5280        if not this:
5281            if self._match_text_seq("UNSIGNED"):
5282                unsigned_type_token = self.SIGNED_TO_UNSIGNED_TYPE_TOKEN.get(type_token)
5283                if not unsigned_type_token:
5284                    self.raise_error(f"Cannot convert {type_token.value} to unsigned.")
5285
5286                type_token = unsigned_type_token or type_token
5287
5288            this = exp.DataType(
5289                this=exp.DataType.Type[type_token.value],
5290                expressions=expressions,
5291                nested=nested,
5292                prefix=prefix,
5293            )
5294
5295            # Empty arrays/structs are allowed
5296            if values is not None:
5297                cls = exp.Struct if is_struct else exp.Array
5298                this = exp.cast(cls(expressions=values), this, copy=False)
5299
5300        elif expressions:
5301            this.set("expressions", expressions)
5302
5303        # https://materialize.com/docs/sql/types/list/#type-name
5304        while self._match(TokenType.LIST):
5305            this = exp.DataType(this=exp.DataType.Type.LIST, expressions=[this], nested=True)
5306
5307        index = self._index
5308
5309        # Postgres supports the INT ARRAY[3] syntax as a synonym for INT[3]
5310        matched_array = self._match(TokenType.ARRAY)
5311
5312        while self._curr:
5313            datatype_token = self._prev.token_type
5314            matched_l_bracket = self._match(TokenType.L_BRACKET)
5315
5316            if (not matched_l_bracket and not matched_array) or (
5317                datatype_token == TokenType.ARRAY and self._match(TokenType.R_BRACKET)
5318            ):
5319                # Postgres allows casting empty arrays such as ARRAY[]::INT[],
5320                # not to be confused with the fixed size array parsing
5321                break
5322
5323            matched_array = False
5324            values = self._parse_csv(self._parse_assignment) or None
5325            if (
5326                values
5327                and not schema
5328                and (
5329                    not self.dialect.SUPPORTS_FIXED_SIZE_ARRAYS or datatype_token == TokenType.ARRAY
5330                )
5331            ):
5332                # Retreating here means that we should not parse the following values as part of the data type, e.g. in DuckDB
5333                # ARRAY[1] should retreat and instead be parsed into exp.Array in contrast to INT[x][y] which denotes a fixed-size array data type
5334                self._retreat(index)
5335                break
5336
5337            this = exp.DataType(
5338                this=exp.DataType.Type.ARRAY, expressions=[this], values=values, nested=True
5339            )
5340            self._match(TokenType.R_BRACKET)
5341
5342        if self.TYPE_CONVERTERS and isinstance(this.this, exp.DataType.Type):
5343            converter = self.TYPE_CONVERTERS.get(this.this)
5344            if converter:
5345                this = converter(t.cast(exp.DataType, this))
5346
5347        return this
5348
5349    def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]:
5350        index = self._index
5351
5352        if (
5353            self._curr
5354            and self._next
5355            and self._curr.token_type in self.TYPE_TOKENS
5356            and self._next.token_type in self.TYPE_TOKENS
5357        ):
5358            # Takes care of special cases like `STRUCT<list ARRAY<...>>` where the identifier is also a
5359            # type token. Without this, the list will be parsed as a type and we'll eventually crash
5360            this = self._parse_id_var()
5361        else:
5362            this = (
5363                self._parse_type(parse_interval=False, fallback_to_identifier=True)
5364                or self._parse_id_var()
5365            )
5366
5367        self._match(TokenType.COLON)
5368
5369        if (
5370            type_required
5371            and not isinstance(this, exp.DataType)
5372            and not self._match_set(self.TYPE_TOKENS, advance=False)
5373        ):
5374            self._retreat(index)
5375            return self._parse_types()
5376
5377        return self._parse_column_def(this)
5378
5379    def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5380        if not self._match_text_seq("AT", "TIME", "ZONE"):
5381            return this
5382        return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary())
5383
5384    def _parse_column(self) -> t.Optional[exp.Expression]:
5385        this = self._parse_column_reference()
5386        column = self._parse_column_ops(this) if this else self._parse_bracket(this)
5387
5388        if self.dialect.SUPPORTS_COLUMN_JOIN_MARKS and column:
5389            column.set("join_mark", self._match(TokenType.JOIN_MARKER))
5390
5391        return column
5392
5393    def _parse_column_reference(self) -> t.Optional[exp.Expression]:
5394        this = self._parse_field()
5395        if (
5396            not this
5397            and self._match(TokenType.VALUES, advance=False)
5398            and self.VALUES_FOLLOWED_BY_PAREN
5399            and (not self._next or self._next.token_type != TokenType.L_PAREN)
5400        ):
5401            this = self._parse_id_var()
5402
5403        if isinstance(this, exp.Identifier):
5404            # We bubble up comments from the Identifier to the Column
5405            this = self.expression(exp.Column, comments=this.pop_comments(), this=this)
5406
5407        return this
5408
5409    def _parse_colon_as_variant_extract(
5410        self, this: t.Optional[exp.Expression]
5411    ) -> t.Optional[exp.Expression]:
5412        casts = []
5413        json_path = []
5414        escape = None
5415
5416        while self._match(TokenType.COLON):
5417            start_index = self._index
5418
5419            # Snowflake allows reserved keywords as json keys but advance_any() excludes TokenType.SELECT from any_tokens=True
5420            path = self._parse_column_ops(
5421                self._parse_field(any_token=True, tokens=(TokenType.SELECT,))
5422            )
5423
5424            # The cast :: operator has a lower precedence than the extraction operator :, so
5425            # we rearrange the AST appropriately to avoid casting the JSON path
5426            while isinstance(path, exp.Cast):
5427                casts.append(path.to)
5428                path = path.this
5429
5430            if casts:
5431                dcolon_offset = next(
5432                    i
5433                    for i, t in enumerate(self._tokens[start_index:])
5434                    if t.token_type == TokenType.DCOLON
5435                )
5436                end_token = self._tokens[start_index + dcolon_offset - 1]
5437            else:
5438                end_token = self._prev
5439
5440            if path:
5441                # Escape single quotes from Snowflake's colon extraction (e.g. col:"a'b") as
5442                # it'll roundtrip to a string literal in GET_PATH
5443                if isinstance(path, exp.Identifier) and path.quoted:
5444                    escape = True
5445
5446                json_path.append(self._find_sql(self._tokens[start_index], end_token))
5447
5448        # The VARIANT extract in Snowflake/Databricks is parsed as a JSONExtract; Snowflake uses the json_path in GET_PATH() while
5449        # Databricks transforms it back to the colon/dot notation
5450        if json_path:
5451            json_path_expr = self.dialect.to_json_path(exp.Literal.string(".".join(json_path)))
5452
5453            if json_path_expr:
5454                json_path_expr.set("escape", escape)
5455
5456            this = self.expression(
5457                exp.JSONExtract,
5458                this=this,
5459                expression=json_path_expr,
5460                variant_extract=True,
5461            )
5462
5463            while casts:
5464                this = self.expression(exp.Cast, this=this, to=casts.pop())
5465
5466        return this
5467
5468    def _parse_dcolon(self) -> t.Optional[exp.Expression]:
5469        return self._parse_types()
5470
5471    def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5472        this = self._parse_bracket(this)
5473
5474        while self._match_set(self.COLUMN_OPERATORS):
5475            op_token = self._prev.token_type
5476            op = self.COLUMN_OPERATORS.get(op_token)
5477
5478            if op_token in (TokenType.DCOLON, TokenType.DOTCOLON):
5479                field = self._parse_dcolon()
5480                if not field:
5481                    self.raise_error("Expected type")
5482            elif op and self._curr:
5483                field = self._parse_column_reference() or self._parse_bracket()
5484                if isinstance(field, exp.Column) and self._match(TokenType.DOT, advance=False):
5485                    field = self._parse_column_ops(field)
5486            else:
5487                field = self._parse_field(any_token=True, anonymous_func=True)
5488
5489            if isinstance(field, (exp.Func, exp.Window)) and this:
5490                # BQ & snowflake allow function calls like x.y.count(...), SAFE.SUBSTR(...) etc
5491                # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules
5492                this = exp.replace_tree(
5493                    this,
5494                    lambda n: (
5495                        self.expression(exp.Dot, this=n.args.get("table"), expression=n.this)
5496                        if n.table
5497                        else n.this
5498                    )
5499                    if isinstance(n, exp.Column)
5500                    else n,
5501                )
5502
5503            if op:
5504                this = op(self, this, field)
5505            elif isinstance(this, exp.Column) and not this.args.get("catalog"):
5506                this = self.expression(
5507                    exp.Column,
5508                    comments=this.comments,
5509                    this=field,
5510                    table=this.this,
5511                    db=this.args.get("table"),
5512                    catalog=this.args.get("db"),
5513                )
5514            elif isinstance(field, exp.Window):
5515                # Move the exp.Dot's to the window's function
5516                window_func = self.expression(exp.Dot, this=this, expression=field.this)
5517                field.set("this", window_func)
5518                this = field
5519            else:
5520                this = self.expression(exp.Dot, this=this, expression=field)
5521
5522            if field and field.comments:
5523                t.cast(exp.Expression, this).add_comments(field.pop_comments())
5524
5525            this = self._parse_bracket(this)
5526
5527        return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
5528
5529    def _parse_primary(self) -> t.Optional[exp.Expression]:
5530        if self._match_set(self.PRIMARY_PARSERS):
5531            token_type = self._prev.token_type
5532            primary = self.PRIMARY_PARSERS[token_type](self, self._prev)
5533
5534            if token_type == TokenType.STRING:
5535                expressions = [primary]
5536                while self._match(TokenType.STRING):
5537                    expressions.append(exp.Literal.string(self._prev.text))
5538
5539                if len(expressions) > 1:
5540                    return self.expression(exp.Concat, expressions=expressions)
5541
5542            return primary
5543
5544        if self._match_pair(TokenType.DOT, TokenType.NUMBER):
5545            return exp.Literal.number(f"0.{self._prev.text}")
5546
5547        if self._match(TokenType.L_PAREN):
5548            comments = self._prev_comments
5549            query = self._parse_select()
5550
5551            if query:
5552                expressions = [query]
5553            else:
5554                expressions = self._parse_expressions()
5555
5556            this = self._parse_query_modifiers(seq_get(expressions, 0))
5557
5558            if not this and self._match(TokenType.R_PAREN, advance=False):
5559                this = self.expression(exp.Tuple)
5560            elif isinstance(this, exp.UNWRAPPED_QUERIES):
5561                this = self._parse_subquery(this=this, parse_alias=False)
5562            elif isinstance(this, exp.Subquery):
5563                this = self._parse_subquery(
5564                    this=self._parse_set_operations(this), parse_alias=False
5565                )
5566            elif len(expressions) > 1 or self._prev.token_type == TokenType.COMMA:
5567                this = self.expression(exp.Tuple, expressions=expressions)
5568            else:
5569                this = self.expression(exp.Paren, this=this)
5570
5571            if this:
5572                this.add_comments(comments)
5573
5574            self._match_r_paren(expression=this)
5575            return this
5576
5577        return None
5578
5579    def _parse_field(
5580        self,
5581        any_token: bool = False,
5582        tokens: t.Optional[t.Collection[TokenType]] = None,
5583        anonymous_func: bool = False,
5584    ) -> t.Optional[exp.Expression]:
5585        if anonymous_func:
5586            field = (
5587                self._parse_function(anonymous=anonymous_func, any_token=any_token)
5588                or self._parse_primary()
5589            )
5590        else:
5591            field = self._parse_primary() or self._parse_function(
5592                anonymous=anonymous_func, any_token=any_token
5593            )
5594        return field or self._parse_id_var(any_token=any_token, tokens=tokens)
5595
5596    def _parse_function(
5597        self,
5598        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5599        anonymous: bool = False,
5600        optional_parens: bool = True,
5601        any_token: bool = False,
5602    ) -> t.Optional[exp.Expression]:
5603        # This allows us to also parse {fn <function>} syntax (Snowflake, MySQL support this)
5604        # See: https://community.snowflake.com/s/article/SQL-Escape-Sequences
5605        fn_syntax = False
5606        if (
5607            self._match(TokenType.L_BRACE, advance=False)
5608            and self._next
5609            and self._next.text.upper() == "FN"
5610        ):
5611            self._advance(2)
5612            fn_syntax = True
5613
5614        func = self._parse_function_call(
5615            functions=functions,
5616            anonymous=anonymous,
5617            optional_parens=optional_parens,
5618            any_token=any_token,
5619        )
5620
5621        if fn_syntax:
5622            self._match(TokenType.R_BRACE)
5623
5624        return func
5625
5626    def _parse_function_call(
5627        self,
5628        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5629        anonymous: bool = False,
5630        optional_parens: bool = True,
5631        any_token: bool = False,
5632    ) -> t.Optional[exp.Expression]:
5633        if not self._curr:
5634            return None
5635
5636        comments = self._curr.comments
5637        token = self._curr
5638        token_type = self._curr.token_type
5639        this = self._curr.text
5640        upper = this.upper()
5641
5642        parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper)
5643        if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS:
5644            self._advance()
5645            return self._parse_window(parser(self))
5646
5647        if not self._next or self._next.token_type != TokenType.L_PAREN:
5648            if optional_parens and token_type in self.NO_PAREN_FUNCTIONS:
5649                self._advance()
5650                return self.expression(self.NO_PAREN_FUNCTIONS[token_type])
5651
5652            return None
5653
5654        if any_token:
5655            if token_type in self.RESERVED_TOKENS:
5656                return None
5657        elif token_type not in self.FUNC_TOKENS:
5658            return None
5659
5660        self._advance(2)
5661
5662        parser = self.FUNCTION_PARSERS.get(upper)
5663        if parser and not anonymous:
5664            this = parser(self)
5665        else:
5666            subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type)
5667
5668            if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH):
5669                this = self.expression(
5670                    subquery_predicate, comments=comments, this=self._parse_select()
5671                )
5672                self._match_r_paren()
5673                return this
5674
5675            if functions is None:
5676                functions = self.FUNCTIONS
5677
5678            function = functions.get(upper)
5679            known_function = function and not anonymous
5680
5681            alias = not known_function or upper in self.FUNCTIONS_WITH_ALIASED_ARGS
5682            args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
5683
5684            post_func_comments = self._curr and self._curr.comments
5685            if known_function and post_func_comments:
5686                # If the user-inputted comment "/* sqlglot.anonymous */" is following the function
5687                # call we'll construct it as exp.Anonymous, even if it's "known"
5688                if any(
5689                    comment.lstrip().startswith(exp.SQLGLOT_ANONYMOUS)
5690                    for comment in post_func_comments
5691                ):
5692                    known_function = False
5693
5694            if alias and known_function:
5695                args = self._kv_to_prop_eq(args)
5696
5697            if known_function:
5698                func_builder = t.cast(t.Callable, function)
5699
5700                if "dialect" in func_builder.__code__.co_varnames:
5701                    func = func_builder(args, dialect=self.dialect)
5702                else:
5703                    func = func_builder(args)
5704
5705                func = self.validate_expression(func, args)
5706                if self.dialect.PRESERVE_ORIGINAL_NAMES:
5707                    func.meta["name"] = this
5708
5709                this = func
5710            else:
5711                if token_type == TokenType.IDENTIFIER:
5712                    this = exp.Identifier(this=this, quoted=True).update_positions(token)
5713                this = self.expression(exp.Anonymous, this=this, expressions=args)
5714
5715        if isinstance(this, exp.Expression):
5716            this.add_comments(comments)
5717
5718        self._match_r_paren(this)
5719        return self._parse_window(this)
5720
5721    def _to_prop_eq(self, expression: exp.Expression, index: int) -> exp.Expression:
5722        return expression
5723
5724    def _kv_to_prop_eq(self, expressions: t.List[exp.Expression]) -> t.List[exp.Expression]:
5725        transformed = []
5726
5727        for index, e in enumerate(expressions):
5728            if isinstance(e, self.KEY_VALUE_DEFINITIONS):
5729                if isinstance(e, exp.Alias):
5730                    e = self.expression(exp.PropertyEQ, this=e.args.get("alias"), expression=e.this)
5731
5732                if not isinstance(e, exp.PropertyEQ):
5733                    e = self.expression(
5734                        exp.PropertyEQ, this=exp.to_identifier(e.this.name), expression=e.expression
5735                    )
5736
5737                if isinstance(e.this, exp.Column):
5738                    e.this.replace(e.this.this)
5739            else:
5740                e = self._to_prop_eq(e, index)
5741
5742            transformed.append(e)
5743
5744        return transformed
5745
5746    def _parse_user_defined_function_expression(self) -> t.Optional[exp.Expression]:
5747        return self._parse_statement()
5748
5749    def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
5750        return self._parse_column_def(this=self._parse_id_var(), computed_column=False)
5751
5752    def _parse_user_defined_function(
5753        self, kind: t.Optional[TokenType] = None
5754    ) -> t.Optional[exp.Expression]:
5755        this = self._parse_table_parts(schema=True)
5756
5757        if not self._match(TokenType.L_PAREN):
5758            return this
5759
5760        expressions = self._parse_csv(self._parse_function_parameter)
5761        self._match_r_paren()
5762        return self.expression(
5763            exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True
5764        )
5765
5766    def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier:
5767        literal = self._parse_primary()
5768        if literal:
5769            return self.expression(exp.Introducer, this=token.text, expression=literal)
5770
5771        return self._identifier_expression(token)
5772
5773    def _parse_session_parameter(self) -> exp.SessionParameter:
5774        kind = None
5775        this = self._parse_id_var() or self._parse_primary()
5776
5777        if this and self._match(TokenType.DOT):
5778            kind = this.name
5779            this = self._parse_var() or self._parse_primary()
5780
5781        return self.expression(exp.SessionParameter, this=this, kind=kind)
5782
5783    def _parse_lambda_arg(self) -> t.Optional[exp.Expression]:
5784        return self._parse_id_var()
5785
5786    def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]:
5787        index = self._index
5788
5789        if self._match(TokenType.L_PAREN):
5790            expressions = t.cast(
5791                t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_lambda_arg)
5792            )
5793
5794            if not self._match(TokenType.R_PAREN):
5795                self._retreat(index)
5796        else:
5797            expressions = [self._parse_lambda_arg()]
5798
5799        if self._match_set(self.LAMBDAS):
5800            return self.LAMBDAS[self._prev.token_type](self, expressions)
5801
5802        self._retreat(index)
5803
5804        this: t.Optional[exp.Expression]
5805
5806        if self._match(TokenType.DISTINCT):
5807            this = self.expression(
5808                exp.Distinct, expressions=self._parse_csv(self._parse_assignment)
5809            )
5810        else:
5811            this = self._parse_select_or_expression(alias=alias)
5812
5813        return self._parse_limit(
5814            self._parse_order(self._parse_having_max(self._parse_respect_or_ignore_nulls(this)))
5815        )
5816
5817    def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
5818        index = self._index
5819        if not self._match(TokenType.L_PAREN):
5820            return this
5821
5822        # Disambiguate between schema and subquery/CTE, e.g. in INSERT INTO table (<expr>),
5823        # expr can be of both types
5824        if self._match_set(self.SELECT_START_TOKENS):
5825            self._retreat(index)
5826            return this
5827        args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def())
5828        self._match_r_paren()
5829        return self.expression(exp.Schema, this=this, expressions=args)
5830
5831    def _parse_field_def(self) -> t.Optional[exp.Expression]:
5832        return self._parse_column_def(self._parse_field(any_token=True))
5833
5834    def _parse_column_def(
5835        self, this: t.Optional[exp.Expression], computed_column: bool = True
5836    ) -> t.Optional[exp.Expression]:
5837        # column defs are not really columns, they're identifiers
5838        if isinstance(this, exp.Column):
5839            this = this.this
5840
5841        if not computed_column:
5842            self._match(TokenType.ALIAS)
5843
5844        kind = self._parse_types(schema=True)
5845
5846        if self._match_text_seq("FOR", "ORDINALITY"):
5847            return self.expression(exp.ColumnDef, this=this, ordinality=True)
5848
5849        constraints: t.List[exp.Expression] = []
5850
5851        if (not kind and self._match(TokenType.ALIAS)) or self._match_texts(
5852            ("ALIAS", "MATERIALIZED")
5853        ):
5854            persisted = self._prev.text.upper() == "MATERIALIZED"
5855            constraint_kind = exp.ComputedColumnConstraint(
5856                this=self._parse_assignment(),
5857                persisted=persisted or self._match_text_seq("PERSISTED"),
5858                not_null=self._match_pair(TokenType.NOT, TokenType.NULL),
5859            )
5860            constraints.append(self.expression(exp.ColumnConstraint, kind=constraint_kind))
5861        elif (
5862            kind
5863            and self._match(TokenType.ALIAS, advance=False)
5864            and (
5865                not self.WRAPPED_TRANSFORM_COLUMN_CONSTRAINT
5866                or (self._next and self._next.token_type == TokenType.L_PAREN)
5867            )
5868        ):
5869            self._advance()
5870            constraints.append(
5871                self.expression(
5872                    exp.ColumnConstraint,
5873                    kind=exp.TransformColumnConstraint(this=self._parse_disjunction()),
5874                )
5875            )
5876
5877        while True:
5878            constraint = self._parse_column_constraint()
5879            if not constraint:
5880                break
5881            constraints.append(constraint)
5882
5883        if not kind and not constraints:
5884            return this
5885
5886        return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints)
5887
5888    def _parse_auto_increment(
5889        self,
5890    ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint:
5891        start = None
5892        increment = None
5893
5894        if self._match(TokenType.L_PAREN, advance=False):
5895            args = self._parse_wrapped_csv(self._parse_bitwise)
5896            start = seq_get(args, 0)
5897            increment = seq_get(args, 1)
5898        elif self._match_text_seq("START"):
5899            start = self._parse_bitwise()
5900            self._match_text_seq("INCREMENT")
5901            increment = self._parse_bitwise()
5902
5903        if start and increment:
5904            return exp.GeneratedAsIdentityColumnConstraint(
5905                start=start, increment=increment, this=False
5906            )
5907
5908        return exp.AutoIncrementColumnConstraint()
5909
5910    def _parse_auto_property(self) -> t.Optional[exp.AutoRefreshProperty]:
5911        if not self._match_text_seq("REFRESH"):
5912            self._retreat(self._index - 1)
5913            return None
5914        return self.expression(exp.AutoRefreshProperty, this=self._parse_var(upper=True))
5915
5916    def _parse_compress(self) -> exp.CompressColumnConstraint:
5917        if self._match(TokenType.L_PAREN, advance=False):
5918            return self.expression(
5919                exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise)
5920            )
5921
5922        return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise())
5923
5924    def _parse_generated_as_identity(
5925        self,
5926    ) -> (
5927        exp.GeneratedAsIdentityColumnConstraint
5928        | exp.ComputedColumnConstraint
5929        | exp.GeneratedAsRowColumnConstraint
5930    ):
5931        if self._match_text_seq("BY", "DEFAULT"):
5932            on_null = self._match_pair(TokenType.ON, TokenType.NULL)
5933            this = self.expression(
5934                exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null
5935            )
5936        else:
5937            self._match_text_seq("ALWAYS")
5938            this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True)
5939
5940        self._match(TokenType.ALIAS)
5941
5942        if self._match_text_seq("ROW"):
5943            start = self._match_text_seq("START")
5944            if not start:
5945                self._match(TokenType.END)
5946            hidden = self._match_text_seq("HIDDEN")
5947            return self.expression(exp.GeneratedAsRowColumnConstraint, start=start, hidden=hidden)
5948
5949        identity = self._match_text_seq("IDENTITY")
5950
5951        if self._match(TokenType.L_PAREN):
5952            if self._match(TokenType.START_WITH):
5953                this.set("start", self._parse_bitwise())
5954            if self._match_text_seq("INCREMENT", "BY"):
5955                this.set("increment", self._parse_bitwise())
5956            if self._match_text_seq("MINVALUE"):
5957                this.set("minvalue", self._parse_bitwise())
5958            if self._match_text_seq("MAXVALUE"):
5959                this.set("maxvalue", self._parse_bitwise())
5960
5961            if self._match_text_seq("CYCLE"):
5962                this.set("cycle", True)
5963            elif self._match_text_seq("NO", "CYCLE"):
5964                this.set("cycle", False)
5965
5966            if not identity:
5967                this.set("expression", self._parse_range())
5968            elif not this.args.get("start") and self._match(TokenType.NUMBER, advance=False):
5969                args = self._parse_csv(self._parse_bitwise)
5970                this.set("start", seq_get(args, 0))
5971                this.set("increment", seq_get(args, 1))
5972
5973            self._match_r_paren()
5974
5975        return this
5976
5977    def _parse_inline(self) -> exp.InlineLengthColumnConstraint:
5978        self._match_text_seq("LENGTH")
5979        return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise())
5980
5981    def _parse_not_constraint(self) -> t.Optional[exp.Expression]:
5982        if self._match_text_seq("NULL"):
5983            return self.expression(exp.NotNullColumnConstraint)
5984        if self._match_text_seq("CASESPECIFIC"):
5985            return self.expression(exp.CaseSpecificColumnConstraint, not_=True)
5986        if self._match_text_seq("FOR", "REPLICATION"):
5987            return self.expression(exp.NotForReplicationColumnConstraint)
5988
5989        # Unconsume the `NOT` token
5990        self._retreat(self._index - 1)
5991        return None
5992
5993    def _parse_column_constraint(self) -> t.Optional[exp.Expression]:
5994        this = self._match(TokenType.CONSTRAINT) and self._parse_id_var()
5995
5996        procedure_option_follows = (
5997            self._match(TokenType.WITH, advance=False)
5998            and self._next
5999            and self._next.text.upper() in self.PROCEDURE_OPTIONS
6000        )
6001
6002        if not procedure_option_follows and self._match_texts(self.CONSTRAINT_PARSERS):
6003            return self.expression(
6004                exp.ColumnConstraint,
6005                this=this,
6006                kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self),
6007            )
6008
6009        return this
6010
6011    def _parse_constraint(self) -> t.Optional[exp.Expression]:
6012        if not self._match(TokenType.CONSTRAINT):
6013            return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS)
6014
6015        return self.expression(
6016            exp.Constraint,
6017            this=self._parse_id_var(),
6018            expressions=self._parse_unnamed_constraints(),
6019        )
6020
6021    def _parse_unnamed_constraints(self) -> t.List[exp.Expression]:
6022        constraints = []
6023        while True:
6024            constraint = self._parse_unnamed_constraint() or self._parse_function()
6025            if not constraint:
6026                break
6027            constraints.append(constraint)
6028
6029        return constraints
6030
6031    def _parse_unnamed_constraint(
6032        self, constraints: t.Optional[t.Collection[str]] = None
6033    ) -> t.Optional[exp.Expression]:
6034        if self._match(TokenType.IDENTIFIER, advance=False) or not self._match_texts(
6035            constraints or self.CONSTRAINT_PARSERS
6036        ):
6037            return None
6038
6039        constraint = self._prev.text.upper()
6040        if constraint not in self.CONSTRAINT_PARSERS:
6041            self.raise_error(f"No parser found for schema constraint {constraint}.")
6042
6043        return self.CONSTRAINT_PARSERS[constraint](self)
6044
6045    def _parse_unique_key(self) -> t.Optional[exp.Expression]:
6046        return self._parse_id_var(any_token=False)
6047
6048    def _parse_unique(self) -> exp.UniqueColumnConstraint:
6049        self._match_text_seq("KEY")
6050        return self.expression(
6051            exp.UniqueColumnConstraint,
6052            nulls=self._match_text_seq("NULLS", "NOT", "DISTINCT"),
6053            this=self._parse_schema(self._parse_unique_key()),
6054            index_type=self._match(TokenType.USING) and self._advance_any() and self._prev.text,
6055            on_conflict=self._parse_on_conflict(),
6056            options=self._parse_key_constraint_options(),
6057        )
6058
6059    def _parse_key_constraint_options(self) -> t.List[str]:
6060        options = []
6061        while True:
6062            if not self._curr:
6063                break
6064
6065            if self._match(TokenType.ON):
6066                action = None
6067                on = self._advance_any() and self._prev.text
6068
6069                if self._match_text_seq("NO", "ACTION"):
6070                    action = "NO ACTION"
6071                elif self._match_text_seq("CASCADE"):
6072                    action = "CASCADE"
6073                elif self._match_text_seq("RESTRICT"):
6074                    action = "RESTRICT"
6075                elif self._match_pair(TokenType.SET, TokenType.NULL):
6076                    action = "SET NULL"
6077                elif self._match_pair(TokenType.SET, TokenType.DEFAULT):
6078                    action = "SET DEFAULT"
6079                else:
6080                    self.raise_error("Invalid key constraint")
6081
6082                options.append(f"ON {on} {action}")
6083            else:
6084                var = self._parse_var_from_options(
6085                    self.KEY_CONSTRAINT_OPTIONS, raise_unmatched=False
6086                )
6087                if not var:
6088                    break
6089                options.append(var.name)
6090
6091        return options
6092
6093    def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]:
6094        if match and not self._match(TokenType.REFERENCES):
6095            return None
6096
6097        expressions = None
6098        this = self._parse_table(schema=True)
6099        options = self._parse_key_constraint_options()
6100        return self.expression(exp.Reference, this=this, expressions=expressions, options=options)
6101
6102    def _parse_foreign_key(self) -> exp.ForeignKey:
6103        expressions = (
6104            self._parse_wrapped_id_vars()
6105            if not self._match(TokenType.REFERENCES, advance=False)
6106            else None
6107        )
6108        reference = self._parse_references()
6109        on_options = {}
6110
6111        while self._match(TokenType.ON):
6112            if not self._match_set((TokenType.DELETE, TokenType.UPDATE)):
6113                self.raise_error("Expected DELETE or UPDATE")
6114
6115            kind = self._prev.text.lower()
6116
6117            if self._match_text_seq("NO", "ACTION"):
6118                action = "NO ACTION"
6119            elif self._match(TokenType.SET):
6120                self._match_set((TokenType.NULL, TokenType.DEFAULT))
6121                action = "SET " + self._prev.text.upper()
6122            else:
6123                self._advance()
6124                action = self._prev.text.upper()
6125
6126            on_options[kind] = action
6127
6128        return self.expression(
6129            exp.ForeignKey,
6130            expressions=expressions,
6131            reference=reference,
6132            options=self._parse_key_constraint_options(),
6133            **on_options,  # type: ignore
6134        )
6135
6136    def _parse_primary_key_part(self) -> t.Optional[exp.Expression]:
6137        return self._parse_ordered() or self._parse_field()
6138
6139    def _parse_period_for_system_time(self) -> t.Optional[exp.PeriodForSystemTimeConstraint]:
6140        if not self._match(TokenType.TIMESTAMP_SNAPSHOT):
6141            self._retreat(self._index - 1)
6142            return None
6143
6144        id_vars = self._parse_wrapped_id_vars()
6145        return self.expression(
6146            exp.PeriodForSystemTimeConstraint,
6147            this=seq_get(id_vars, 0),
6148            expression=seq_get(id_vars, 1),
6149        )
6150
6151    def _parse_primary_key(
6152        self, wrapped_optional: bool = False, in_props: bool = False
6153    ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey:
6154        desc = (
6155            self._match_set((TokenType.ASC, TokenType.DESC))
6156            and self._prev.token_type == TokenType.DESC
6157        )
6158
6159        if not in_props and not self._match(TokenType.L_PAREN, advance=False):
6160            return self.expression(
6161                exp.PrimaryKeyColumnConstraint,
6162                desc=desc,
6163                options=self._parse_key_constraint_options(),
6164            )
6165
6166        expressions = self._parse_wrapped_csv(
6167            self._parse_primary_key_part, optional=wrapped_optional
6168        )
6169        options = self._parse_key_constraint_options()
6170        return self.expression(exp.PrimaryKey, expressions=expressions, options=options)
6171
6172    def _parse_bracket_key_value(self, is_map: bool = False) -> t.Optional[exp.Expression]:
6173        return self._parse_slice(self._parse_alias(self._parse_assignment(), explicit=True))
6174
6175    def _parse_odbc_datetime_literal(self) -> exp.Expression:
6176        """
6177        Parses a datetime column in ODBC format. We parse the column into the corresponding
6178        types, for example `{d'yyyy-mm-dd'}` will be parsed as a `Date` column, exactly the
6179        same as we did for `DATE('yyyy-mm-dd')`.
6180
6181        Reference:
6182        https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals
6183        """
6184        self._match(TokenType.VAR)
6185        exp_class = self.ODBC_DATETIME_LITERALS[self._prev.text.lower()]
6186        expression = self.expression(exp_class=exp_class, this=self._parse_string())
6187        if not self._match(TokenType.R_BRACE):
6188            self.raise_error("Expected }")
6189        return expression
6190
6191    def _parse_bracket(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
6192        if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)):
6193            return this
6194
6195        bracket_kind = self._prev.token_type
6196        if (
6197            bracket_kind == TokenType.L_BRACE
6198            and self._curr
6199            and self._curr.token_type == TokenType.VAR
6200            and self._curr.text.lower() in self.ODBC_DATETIME_LITERALS
6201        ):
6202            return self._parse_odbc_datetime_literal()
6203
6204        expressions = self._parse_csv(
6205            lambda: self._parse_bracket_key_value(is_map=bracket_kind == TokenType.L_BRACE)
6206        )
6207
6208        if bracket_kind == TokenType.L_BRACKET and not self._match(TokenType.R_BRACKET):
6209            self.raise_error("Expected ]")
6210        elif bracket_kind == TokenType.L_BRACE and not self._match(TokenType.R_BRACE):
6211            self.raise_error("Expected }")
6212
6213        # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
6214        if bracket_kind == TokenType.L_BRACE:
6215            this = self.expression(exp.Struct, expressions=self._kv_to_prop_eq(expressions))
6216        elif not this:
6217            this = build_array_constructor(
6218                exp.Array, args=expressions, bracket_kind=bracket_kind, dialect=self.dialect
6219            )
6220        else:
6221            constructor_type = self.ARRAY_CONSTRUCTORS.get(this.name.upper())
6222            if constructor_type:
6223                return build_array_constructor(
6224                    constructor_type,
6225                    args=expressions,
6226                    bracket_kind=bracket_kind,
6227                    dialect=self.dialect,
6228                )
6229
6230            expressions = apply_index_offset(
6231                this, expressions, -self.dialect.INDEX_OFFSET, dialect=self.dialect
6232            )
6233            this = self.expression(exp.Bracket, this=this, expressions=expressions)
6234
6235        self._add_comments(this)
6236        return self._parse_bracket(this)
6237
6238    def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6239        if self._match(TokenType.COLON):
6240            return self.expression(exp.Slice, this=this, expression=self._parse_assignment())
6241        return this
6242
6243    def _parse_case(self) -> t.Optional[exp.Expression]:
6244        ifs = []
6245        default = None
6246
6247        comments = self._prev_comments
6248        expression = self._parse_assignment()
6249
6250        while self._match(TokenType.WHEN):
6251            this = self._parse_assignment()
6252            self._match(TokenType.THEN)
6253            then = self._parse_assignment()
6254            ifs.append(self.expression(exp.If, this=this, true=then))
6255
6256        if self._match(TokenType.ELSE):
6257            default = self._parse_assignment()
6258
6259        if not self._match(TokenType.END):
6260            if isinstance(default, exp.Interval) and default.this.sql().upper() == "END":
6261                default = exp.column("interval")
6262            else:
6263                self.raise_error("Expected END after CASE", self._prev)
6264
6265        return self.expression(
6266            exp.Case, comments=comments, this=expression, ifs=ifs, default=default
6267        )
6268
6269    def _parse_if(self) -> t.Optional[exp.Expression]:
6270        if self._match(TokenType.L_PAREN):
6271            args = self._parse_csv(
6272                lambda: self._parse_alias(self._parse_assignment(), explicit=True)
6273            )
6274            this = self.validate_expression(exp.If.from_arg_list(args), args)
6275            self._match_r_paren()
6276        else:
6277            index = self._index - 1
6278
6279            if self.NO_PAREN_IF_COMMANDS and index == 0:
6280                return self._parse_as_command(self._prev)
6281
6282            condition = self._parse_assignment()
6283
6284            if not condition:
6285                self._retreat(index)
6286                return None
6287
6288            self._match(TokenType.THEN)
6289            true = self._parse_assignment()
6290            false = self._parse_assignment() if self._match(TokenType.ELSE) else None
6291            self._match(TokenType.END)
6292            this = self.expression(exp.If, this=condition, true=true, false=false)
6293
6294        return this
6295
6296    def _parse_next_value_for(self) -> t.Optional[exp.Expression]:
6297        if not self._match_text_seq("VALUE", "FOR"):
6298            self._retreat(self._index - 1)
6299            return None
6300
6301        return self.expression(
6302            exp.NextValueFor,
6303            this=self._parse_column(),
6304            order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order),
6305        )
6306
6307    def _parse_extract(self) -> exp.Extract | exp.Anonymous:
6308        this = self._parse_function() or self._parse_var_or_string(upper=True)
6309
6310        if self._match(TokenType.FROM):
6311            return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6312
6313        if not self._match(TokenType.COMMA):
6314            self.raise_error("Expected FROM or comma after EXTRACT", self._prev)
6315
6316        return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6317
6318    def _parse_gap_fill(self) -> exp.GapFill:
6319        self._match(TokenType.TABLE)
6320        this = self._parse_table()
6321
6322        self._match(TokenType.COMMA)
6323        args = [this, *self._parse_csv(self._parse_lambda)]
6324
6325        gap_fill = exp.GapFill.from_arg_list(args)
6326        return self.validate_expression(gap_fill, args)
6327
6328    def _parse_cast(self, strict: bool, safe: t.Optional[bool] = None) -> exp.Expression:
6329        this = self._parse_assignment()
6330
6331        if not self._match(TokenType.ALIAS):
6332            if self._match(TokenType.COMMA):
6333                return self.expression(exp.CastToStrType, this=this, to=self._parse_string())
6334
6335            self.raise_error("Expected AS after CAST")
6336
6337        fmt = None
6338        to = self._parse_types()
6339
6340        default = self._match(TokenType.DEFAULT)
6341        if default:
6342            default = self._parse_bitwise()
6343            self._match_text_seq("ON", "CONVERSION", "ERROR")
6344
6345        if self._match_set((TokenType.FORMAT, TokenType.COMMA)):
6346            fmt_string = self._parse_string()
6347            fmt = self._parse_at_time_zone(fmt_string)
6348
6349            if not to:
6350                to = exp.DataType.build(exp.DataType.Type.UNKNOWN)
6351            if to.this in exp.DataType.TEMPORAL_TYPES:
6352                this = self.expression(
6353                    exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime,
6354                    this=this,
6355                    format=exp.Literal.string(
6356                        format_time(
6357                            fmt_string.this if fmt_string else "",
6358                            self.dialect.FORMAT_MAPPING or self.dialect.TIME_MAPPING,
6359                            self.dialect.FORMAT_TRIE or self.dialect.TIME_TRIE,
6360                        )
6361                    ),
6362                    safe=safe,
6363                )
6364
6365                if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime):
6366                    this.set("zone", fmt.args["zone"])
6367                return this
6368        elif not to:
6369            self.raise_error("Expected TYPE after CAST")
6370        elif isinstance(to, exp.Identifier):
6371            to = exp.DataType.build(to.name, udt=True)
6372        elif to.this == exp.DataType.Type.CHAR:
6373            if self._match(TokenType.CHARACTER_SET):
6374                to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
6375
6376        return self.expression(
6377            exp.Cast if strict else exp.TryCast,
6378            this=this,
6379            to=to,
6380            format=fmt,
6381            safe=safe,
6382            action=self._parse_var_from_options(self.CAST_ACTIONS, raise_unmatched=False),
6383            default=default,
6384        )
6385
6386    def _parse_string_agg(self) -> exp.GroupConcat:
6387        if self._match(TokenType.DISTINCT):
6388            args: t.List[t.Optional[exp.Expression]] = [
6389                self.expression(exp.Distinct, expressions=[self._parse_assignment()])
6390            ]
6391            if self._match(TokenType.COMMA):
6392                args.extend(self._parse_csv(self._parse_assignment))
6393        else:
6394            args = self._parse_csv(self._parse_assignment)  # type: ignore
6395
6396        if self._match_text_seq("ON", "OVERFLOW"):
6397            # trino: LISTAGG(expression [, separator] [ON OVERFLOW overflow_behavior])
6398            if self._match_text_seq("ERROR"):
6399                on_overflow: t.Optional[exp.Expression] = exp.var("ERROR")
6400            else:
6401                self._match_text_seq("TRUNCATE")
6402                on_overflow = self.expression(
6403                    exp.OverflowTruncateBehavior,
6404                    this=self._parse_string(),
6405                    with_count=(
6406                        self._match_text_seq("WITH", "COUNT")
6407                        or not self._match_text_seq("WITHOUT", "COUNT")
6408                    ),
6409                )
6410        else:
6411            on_overflow = None
6412
6413        index = self._index
6414        if not self._match(TokenType.R_PAREN) and args:
6415            # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]])
6416            # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n])
6417            # The order is parsed through `this` as a canonicalization for WITHIN GROUPs
6418            args[0] = self._parse_limit(this=self._parse_order(this=args[0]))
6419            return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1))
6420
6421        # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]).
6422        # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that
6423        # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them.
6424        if not self._match_text_seq("WITHIN", "GROUP"):
6425            self._retreat(index)
6426            return self.validate_expression(exp.GroupConcat.from_arg_list(args), args)
6427
6428        # The corresponding match_r_paren will be called in parse_function (caller)
6429        self._match_l_paren()
6430
6431        return self.expression(
6432            exp.GroupConcat,
6433            this=self._parse_order(this=seq_get(args, 0)),
6434            separator=seq_get(args, 1),
6435            on_overflow=on_overflow,
6436        )
6437
6438    def _parse_convert(
6439        self, strict: bool, safe: t.Optional[bool] = None
6440    ) -> t.Optional[exp.Expression]:
6441        this = self._parse_bitwise()
6442
6443        if self._match(TokenType.USING):
6444            to: t.Optional[exp.Expression] = self.expression(
6445                exp.CharacterSet, this=self._parse_var()
6446            )
6447        elif self._match(TokenType.COMMA):
6448            to = self._parse_types()
6449        else:
6450            to = None
6451
6452        return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, safe=safe)
6453
6454    def _parse_xml_table(self) -> exp.XMLTable:
6455        namespaces = None
6456        passing = None
6457        columns = None
6458
6459        if self._match_text_seq("XMLNAMESPACES", "("):
6460            namespaces = self._parse_xml_namespace()
6461            self._match_text_seq(")", ",")
6462
6463        this = self._parse_string()
6464
6465        if self._match_text_seq("PASSING"):
6466            # The BY VALUE keywords are optional and are provided for semantic clarity
6467            self._match_text_seq("BY", "VALUE")
6468            passing = self._parse_csv(self._parse_column)
6469
6470        by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
6471
6472        if self._match_text_seq("COLUMNS"):
6473            columns = self._parse_csv(self._parse_field_def)
6474
6475        return self.expression(
6476            exp.XMLTable,
6477            this=this,
6478            namespaces=namespaces,
6479            passing=passing,
6480            columns=columns,
6481            by_ref=by_ref,
6482        )
6483
6484    def _parse_xml_namespace(self) -> t.List[exp.XMLNamespace]:
6485        namespaces = []
6486
6487        while True:
6488            if self._match(TokenType.DEFAULT):
6489                uri = self._parse_string()
6490            else:
6491                uri = self._parse_alias(self._parse_string())
6492            namespaces.append(self.expression(exp.XMLNamespace, this=uri))
6493            if not self._match(TokenType.COMMA):
6494                break
6495
6496        return namespaces
6497
6498    def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]:
6499        """
6500        There are generally two variants of the DECODE function:
6501
6502        - DECODE(bin, charset)
6503        - DECODE(expression, search, result [, search, result] ... [, default])
6504
6505        The second variant will always be parsed into a CASE expression. Note that NULL
6506        needs special treatment, since we need to explicitly check for it with `IS NULL`,
6507        instead of relying on pattern matching.
6508        """
6509        args = self._parse_csv(self._parse_assignment)
6510
6511        if len(args) < 3:
6512            return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1))
6513
6514        expression, *expressions = args
6515        if not expression:
6516            return None
6517
6518        ifs = []
6519        for search, result in zip(expressions[::2], expressions[1::2]):
6520            if not search or not result:
6521                return None
6522
6523            if isinstance(search, exp.Literal):
6524                ifs.append(
6525                    exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result)
6526                )
6527            elif isinstance(search, exp.Null):
6528                ifs.append(
6529                    exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result)
6530                )
6531            else:
6532                cond = exp.or_(
6533                    exp.EQ(this=expression.copy(), expression=search),
6534                    exp.and_(
6535                        exp.Is(this=expression.copy(), expression=exp.Null()),
6536                        exp.Is(this=search.copy(), expression=exp.Null()),
6537                        copy=False,
6538                    ),
6539                    copy=False,
6540                )
6541                ifs.append(exp.If(this=cond, true=result))
6542
6543        return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
6544
6545    def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]:
6546        self._match_text_seq("KEY")
6547        key = self._parse_column()
6548        self._match_set(self.JSON_KEY_VALUE_SEPARATOR_TOKENS)
6549        self._match_text_seq("VALUE")
6550        value = self._parse_bitwise()
6551
6552        if not key and not value:
6553            return None
6554        return self.expression(exp.JSONKeyValue, this=key, expression=value)
6555
6556    def _parse_format_json(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6557        if not this or not self._match_text_seq("FORMAT", "JSON"):
6558            return this
6559
6560        return self.expression(exp.FormatJson, this=this)
6561
6562    def _parse_on_condition(self) -> t.Optional[exp.OnCondition]:
6563        # MySQL uses "X ON EMPTY Y ON ERROR" (e.g. JSON_VALUE) while Oracle uses the opposite (e.g. JSON_EXISTS)
6564        if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR:
6565            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6566            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6567        else:
6568            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6569            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6570
6571        null = self._parse_on_handling("NULL", *self.ON_CONDITION_TOKENS)
6572
6573        if not empty and not error and not null:
6574            return None
6575
6576        return self.expression(
6577            exp.OnCondition,
6578            empty=empty,
6579            error=error,
6580            null=null,
6581        )
6582
6583    def _parse_on_handling(
6584        self, on: str, *values: str
6585    ) -> t.Optional[str] | t.Optional[exp.Expression]:
6586        # Parses the "X ON Y" or "DEFAULT <expr> ON Y syntax, e.g. NULL ON NULL (Oracle, T-SQL, MySQL)
6587        for value in values:
6588            if self._match_text_seq(value, "ON", on):
6589                return f"{value} ON {on}"
6590
6591        index = self._index
6592        if self._match(TokenType.DEFAULT):
6593            default_value = self._parse_bitwise()
6594            if self._match_text_seq("ON", on):
6595                return default_value
6596
6597            self._retreat(index)
6598
6599        return None
6600
6601    @t.overload
6602    def _parse_json_object(self, agg: Lit[False]) -> exp.JSONObject: ...
6603
6604    @t.overload
6605    def _parse_json_object(self, agg: Lit[True]) -> exp.JSONObjectAgg: ...
6606
6607    def _parse_json_object(self, agg=False):
6608        star = self._parse_star()
6609        expressions = (
6610            [star]
6611            if star
6612            else self._parse_csv(lambda: self._parse_format_json(self._parse_json_key_value()))
6613        )
6614        null_handling = self._parse_on_handling("NULL", "NULL", "ABSENT")
6615
6616        unique_keys = None
6617        if self._match_text_seq("WITH", "UNIQUE"):
6618            unique_keys = True
6619        elif self._match_text_seq("WITHOUT", "UNIQUE"):
6620            unique_keys = False
6621
6622        self._match_text_seq("KEYS")
6623
6624        return_type = self._match_text_seq("RETURNING") and self._parse_format_json(
6625            self._parse_type()
6626        )
6627        encoding = self._match_text_seq("ENCODING") and self._parse_var()
6628
6629        return self.expression(
6630            exp.JSONObjectAgg if agg else exp.JSONObject,
6631            expressions=expressions,
6632            null_handling=null_handling,
6633            unique_keys=unique_keys,
6634            return_type=return_type,
6635            encoding=encoding,
6636        )
6637
6638    # Note: this is currently incomplete; it only implements the "JSON_value_column" part
6639    def _parse_json_column_def(self) -> exp.JSONColumnDef:
6640        if not self._match_text_seq("NESTED"):
6641            this = self._parse_id_var()
6642            kind = self._parse_types(allow_identifiers=False)
6643            nested = None
6644        else:
6645            this = None
6646            kind = None
6647            nested = True
6648
6649        path = self._match_text_seq("PATH") and self._parse_string()
6650        nested_schema = nested and self._parse_json_schema()
6651
6652        return self.expression(
6653            exp.JSONColumnDef,
6654            this=this,
6655            kind=kind,
6656            path=path,
6657            nested_schema=nested_schema,
6658        )
6659
6660    def _parse_json_schema(self) -> exp.JSONSchema:
6661        self._match_text_seq("COLUMNS")
6662        return self.expression(
6663            exp.JSONSchema,
6664            expressions=self._parse_wrapped_csv(self._parse_json_column_def, optional=True),
6665        )
6666
6667    def _parse_json_table(self) -> exp.JSONTable:
6668        this = self._parse_format_json(self._parse_bitwise())
6669        path = self._match(TokenType.COMMA) and self._parse_string()
6670        error_handling = self._parse_on_handling("ERROR", "ERROR", "NULL")
6671        empty_handling = self._parse_on_handling("EMPTY", "ERROR", "NULL")
6672        schema = self._parse_json_schema()
6673
6674        return exp.JSONTable(
6675            this=this,
6676            schema=schema,
6677            path=path,
6678            error_handling=error_handling,
6679            empty_handling=empty_handling,
6680        )
6681
6682    def _parse_match_against(self) -> exp.MatchAgainst:
6683        expressions = self._parse_csv(self._parse_column)
6684
6685        self._match_text_seq(")", "AGAINST", "(")
6686
6687        this = self._parse_string()
6688
6689        if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"):
6690            modifier = "IN NATURAL LANGUAGE MODE"
6691            if self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6692                modifier = f"{modifier} WITH QUERY EXPANSION"
6693        elif self._match_text_seq("IN", "BOOLEAN", "MODE"):
6694            modifier = "IN BOOLEAN MODE"
6695        elif self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6696            modifier = "WITH QUERY EXPANSION"
6697        else:
6698            modifier = None
6699
6700        return self.expression(
6701            exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier
6702        )
6703
6704    # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16
6705    def _parse_open_json(self) -> exp.OpenJSON:
6706        this = self._parse_bitwise()
6707        path = self._match(TokenType.COMMA) and self._parse_string()
6708
6709        def _parse_open_json_column_def() -> exp.OpenJSONColumnDef:
6710            this = self._parse_field(any_token=True)
6711            kind = self._parse_types()
6712            path = self._parse_string()
6713            as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON)
6714
6715            return self.expression(
6716                exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json
6717            )
6718
6719        expressions = None
6720        if self._match_pair(TokenType.R_PAREN, TokenType.WITH):
6721            self._match_l_paren()
6722            expressions = self._parse_csv(_parse_open_json_column_def)
6723
6724        return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions)
6725
6726    def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition:
6727        args = self._parse_csv(self._parse_bitwise)
6728
6729        if self._match(TokenType.IN):
6730            return self.expression(
6731                exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0)
6732            )
6733
6734        if haystack_first:
6735            haystack = seq_get(args, 0)
6736            needle = seq_get(args, 1)
6737        else:
6738            haystack = seq_get(args, 1)
6739            needle = seq_get(args, 0)
6740
6741        return self.expression(
6742            exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2)
6743        )
6744
6745    def _parse_predict(self) -> exp.Predict:
6746        self._match_text_seq("MODEL")
6747        this = self._parse_table()
6748
6749        self._match(TokenType.COMMA)
6750        self._match_text_seq("TABLE")
6751
6752        return self.expression(
6753            exp.Predict,
6754            this=this,
6755            expression=self._parse_table(),
6756            params_struct=self._match(TokenType.COMMA) and self._parse_bitwise(),
6757        )
6758
6759    def _parse_join_hint(self, func_name: str) -> exp.JoinHint:
6760        args = self._parse_csv(self._parse_table)
6761        return exp.JoinHint(this=func_name.upper(), expressions=args)
6762
6763    def _parse_substring(self) -> exp.Substring:
6764        # Postgres supports the form: substring(string [from int] [for int])
6765        # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6
6766
6767        args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise))
6768
6769        if self._match(TokenType.FROM):
6770            args.append(self._parse_bitwise())
6771        if self._match(TokenType.FOR):
6772            if len(args) == 1:
6773                args.append(exp.Literal.number(1))
6774            args.append(self._parse_bitwise())
6775
6776        return self.validate_expression(exp.Substring.from_arg_list(args), args)
6777
6778    def _parse_trim(self) -> exp.Trim:
6779        # https://www.w3resource.com/sql/character-functions/trim.php
6780        # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html
6781
6782        position = None
6783        collation = None
6784        expression = None
6785
6786        if self._match_texts(self.TRIM_TYPES):
6787            position = self._prev.text.upper()
6788
6789        this = self._parse_bitwise()
6790        if self._match_set((TokenType.FROM, TokenType.COMMA)):
6791            invert_order = self._prev.token_type == TokenType.FROM or self.TRIM_PATTERN_FIRST
6792            expression = self._parse_bitwise()
6793
6794            if invert_order:
6795                this, expression = expression, this
6796
6797        if self._match(TokenType.COLLATE):
6798            collation = self._parse_bitwise()
6799
6800        return self.expression(
6801            exp.Trim, this=this, position=position, expression=expression, collation=collation
6802        )
6803
6804    def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]:
6805        return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window)
6806
6807    def _parse_named_window(self) -> t.Optional[exp.Expression]:
6808        return self._parse_window(self._parse_id_var(), alias=True)
6809
6810    def _parse_respect_or_ignore_nulls(
6811        self, this: t.Optional[exp.Expression]
6812    ) -> t.Optional[exp.Expression]:
6813        if self._match_text_seq("IGNORE", "NULLS"):
6814            return self.expression(exp.IgnoreNulls, this=this)
6815        if self._match_text_seq("RESPECT", "NULLS"):
6816            return self.expression(exp.RespectNulls, this=this)
6817        return this
6818
6819    def _parse_having_max(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6820        if self._match(TokenType.HAVING):
6821            self._match_texts(("MAX", "MIN"))
6822            max = self._prev.text.upper() != "MIN"
6823            return self.expression(
6824                exp.HavingMax, this=this, expression=self._parse_column(), max=max
6825            )
6826
6827        return this
6828
6829    def _parse_window(
6830        self, this: t.Optional[exp.Expression], alias: bool = False
6831    ) -> t.Optional[exp.Expression]:
6832        func = this
6833        comments = func.comments if isinstance(func, exp.Expression) else None
6834
6835        # T-SQL allows the OVER (...) syntax after WITHIN GROUP.
6836        # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16
6837        if self._match_text_seq("WITHIN", "GROUP"):
6838            order = self._parse_wrapped(self._parse_order)
6839            this = self.expression(exp.WithinGroup, this=this, expression=order)
6840
6841        if self._match_pair(TokenType.FILTER, TokenType.L_PAREN):
6842            self._match(TokenType.WHERE)
6843            this = self.expression(
6844                exp.Filter, this=this, expression=self._parse_where(skip_where_token=True)
6845            )
6846            self._match_r_paren()
6847
6848        # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER
6849        # Some dialects choose to implement and some do not.
6850        # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
6851
6852        # There is some code above in _parse_lambda that handles
6853        #   SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ...
6854
6855        # The below changes handle
6856        #   SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ...
6857
6858        # Oracle allows both formats
6859        #   (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html)
6860        #   and Snowflake chose to do the same for familiarity
6861        #   https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes
6862        if isinstance(this, exp.AggFunc):
6863            ignore_respect = this.find(exp.IgnoreNulls, exp.RespectNulls)
6864
6865            if ignore_respect and ignore_respect is not this:
6866                ignore_respect.replace(ignore_respect.this)
6867                this = self.expression(ignore_respect.__class__, this=this)
6868
6869        this = self._parse_respect_or_ignore_nulls(this)
6870
6871        # bigquery select from window x AS (partition by ...)
6872        if alias:
6873            over = None
6874            self._match(TokenType.ALIAS)
6875        elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS):
6876            return this
6877        else:
6878            over = self._prev.text.upper()
6879
6880        if comments and isinstance(func, exp.Expression):
6881            func.pop_comments()
6882
6883        if not self._match(TokenType.L_PAREN):
6884            return self.expression(
6885                exp.Window,
6886                comments=comments,
6887                this=this,
6888                alias=self._parse_id_var(False),
6889                over=over,
6890            )
6891
6892        window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS)
6893
6894        first = self._match(TokenType.FIRST)
6895        if self._match_text_seq("LAST"):
6896            first = False
6897
6898        partition, order = self._parse_partition_and_order()
6899        kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text
6900
6901        if kind:
6902            self._match(TokenType.BETWEEN)
6903            start = self._parse_window_spec()
6904            self._match(TokenType.AND)
6905            end = self._parse_window_spec()
6906            exclude = (
6907                self._parse_var_from_options(self.WINDOW_EXCLUDE_OPTIONS)
6908                if self._match_text_seq("EXCLUDE")
6909                else None
6910            )
6911
6912            spec = self.expression(
6913                exp.WindowSpec,
6914                kind=kind,
6915                start=start["value"],
6916                start_side=start["side"],
6917                end=end["value"],
6918                end_side=end["side"],
6919                exclude=exclude,
6920            )
6921        else:
6922            spec = None
6923
6924        self._match_r_paren()
6925
6926        window = self.expression(
6927            exp.Window,
6928            comments=comments,
6929            this=this,
6930            partition_by=partition,
6931            order=order,
6932            spec=spec,
6933            alias=window_alias,
6934            over=over,
6935            first=first,
6936        )
6937
6938        # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...)
6939        if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False):
6940            return self._parse_window(window, alias=alias)
6941
6942        return window
6943
6944    def _parse_partition_and_order(
6945        self,
6946    ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]:
6947        return self._parse_partition_by(), self._parse_order()
6948
6949    def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]:
6950        self._match(TokenType.BETWEEN)
6951
6952        return {
6953            "value": (
6954                (self._match_text_seq("UNBOUNDED") and "UNBOUNDED")
6955                or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW")
6956                or self._parse_bitwise()
6957            ),
6958            "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text,
6959        }
6960
6961    def _parse_alias(
6962        self, this: t.Optional[exp.Expression], explicit: bool = False
6963    ) -> t.Optional[exp.Expression]:
6964        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
6965        # so this section tries to parse the clause version and if it fails, it treats the token
6966        # as an identifier (alias)
6967        if self._can_parse_limit_or_offset():
6968            return this
6969
6970        any_token = self._match(TokenType.ALIAS)
6971        comments = self._prev_comments or []
6972
6973        if explicit and not any_token:
6974            return this
6975
6976        if self._match(TokenType.L_PAREN):
6977            aliases = self.expression(
6978                exp.Aliases,
6979                comments=comments,
6980                this=this,
6981                expressions=self._parse_csv(lambda: self._parse_id_var(any_token)),
6982            )
6983            self._match_r_paren(aliases)
6984            return aliases
6985
6986        alias = self._parse_id_var(any_token, tokens=self.ALIAS_TOKENS) or (
6987            self.STRING_ALIASES and self._parse_string_as_identifier()
6988        )
6989
6990        if alias:
6991            comments.extend(alias.pop_comments())
6992            this = self.expression(exp.Alias, comments=comments, this=this, alias=alias)
6993            column = this.this
6994
6995            # Moves the comment next to the alias in `expr /* comment */ AS alias`
6996            if not this.comments and column and column.comments:
6997                this.comments = column.pop_comments()
6998
6999        return this
7000
7001    def _parse_id_var(
7002        self,
7003        any_token: bool = True,
7004        tokens: t.Optional[t.Collection[TokenType]] = None,
7005    ) -> t.Optional[exp.Expression]:
7006        expression = self._parse_identifier()
7007        if not expression and (
7008            (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS)
7009        ):
7010            quoted = self._prev.token_type == TokenType.STRING
7011            expression = self._identifier_expression(quoted=quoted)
7012
7013        return expression
7014
7015    def _parse_string(self) -> t.Optional[exp.Expression]:
7016        if self._match_set(self.STRING_PARSERS):
7017            return self.STRING_PARSERS[self._prev.token_type](self, self._prev)
7018        return self._parse_placeholder()
7019
7020    def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]:
7021        output = exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True)
7022        if output:
7023            output.update_positions(self._prev)
7024        return output
7025
7026    def _parse_number(self) -> t.Optional[exp.Expression]:
7027        if self._match_set(self.NUMERIC_PARSERS):
7028            return self.NUMERIC_PARSERS[self._prev.token_type](self, self._prev)
7029        return self._parse_placeholder()
7030
7031    def _parse_identifier(self) -> t.Optional[exp.Expression]:
7032        if self._match(TokenType.IDENTIFIER):
7033            return self._identifier_expression(quoted=True)
7034        return self._parse_placeholder()
7035
7036    def _parse_var(
7037        self,
7038        any_token: bool = False,
7039        tokens: t.Optional[t.Collection[TokenType]] = None,
7040        upper: bool = False,
7041    ) -> t.Optional[exp.Expression]:
7042        if (
7043            (any_token and self._advance_any())
7044            or self._match(TokenType.VAR)
7045            or (self._match_set(tokens) if tokens else False)
7046        ):
7047            return self.expression(
7048                exp.Var, this=self._prev.text.upper() if upper else self._prev.text
7049            )
7050        return self._parse_placeholder()
7051
7052    def _advance_any(self, ignore_reserved: bool = False) -> t.Optional[Token]:
7053        if self._curr and (ignore_reserved or self._curr.token_type not in self.RESERVED_TOKENS):
7054            self._advance()
7055            return self._prev
7056        return None
7057
7058    def _parse_var_or_string(self, upper: bool = False) -> t.Optional[exp.Expression]:
7059        return self._parse_string() or self._parse_var(any_token=True, upper=upper)
7060
7061    def _parse_primary_or_var(self) -> t.Optional[exp.Expression]:
7062        return self._parse_primary() or self._parse_var(any_token=True)
7063
7064    def _parse_null(self) -> t.Optional[exp.Expression]:
7065        if self._match_set(self.NULL_TOKENS):
7066            return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev)
7067        return self._parse_placeholder()
7068
7069    def _parse_boolean(self) -> t.Optional[exp.Expression]:
7070        if self._match(TokenType.TRUE):
7071            return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev)
7072        if self._match(TokenType.FALSE):
7073            return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev)
7074        return self._parse_placeholder()
7075
7076    def _parse_star(self) -> t.Optional[exp.Expression]:
7077        if self._match(TokenType.STAR):
7078            return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev)
7079        return self._parse_placeholder()
7080
7081    def _parse_parameter(self) -> exp.Parameter:
7082        this = self._parse_identifier() or self._parse_primary_or_var()
7083        return self.expression(exp.Parameter, this=this)
7084
7085    def _parse_placeholder(self) -> t.Optional[exp.Expression]:
7086        if self._match_set(self.PLACEHOLDER_PARSERS):
7087            placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self)
7088            if placeholder:
7089                return placeholder
7090            self._advance(-1)
7091        return None
7092
7093    def _parse_star_op(self, *keywords: str) -> t.Optional[t.List[exp.Expression]]:
7094        if not self._match_texts(keywords):
7095            return None
7096        if self._match(TokenType.L_PAREN, advance=False):
7097            return self._parse_wrapped_csv(self._parse_expression)
7098
7099        expression = self._parse_expression()
7100        return [expression] if expression else None
7101
7102    def _parse_csv(
7103        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA
7104    ) -> t.List[exp.Expression]:
7105        parse_result = parse_method()
7106        items = [parse_result] if parse_result is not None else []
7107
7108        while self._match(sep):
7109            self._add_comments(parse_result)
7110            parse_result = parse_method()
7111            if parse_result is not None:
7112                items.append(parse_result)
7113
7114        return items
7115
7116    def _parse_tokens(
7117        self, parse_method: t.Callable, expressions: t.Dict
7118    ) -> t.Optional[exp.Expression]:
7119        this = parse_method()
7120
7121        while self._match_set(expressions):
7122            this = self.expression(
7123                expressions[self._prev.token_type],
7124                this=this,
7125                comments=self._prev_comments,
7126                expression=parse_method(),
7127            )
7128
7129        return this
7130
7131    def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]:
7132        return self._parse_wrapped_csv(self._parse_id_var, optional=optional)
7133
7134    def _parse_wrapped_csv(
7135        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False
7136    ) -> t.List[exp.Expression]:
7137        return self._parse_wrapped(
7138            lambda: self._parse_csv(parse_method, sep=sep), optional=optional
7139        )
7140
7141    def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any:
7142        wrapped = self._match(TokenType.L_PAREN)
7143        if not wrapped and not optional:
7144            self.raise_error("Expecting (")
7145        parse_result = parse_method()
7146        if wrapped:
7147            self._match_r_paren()
7148        return parse_result
7149
7150    def _parse_expressions(self) -> t.List[exp.Expression]:
7151        return self._parse_csv(self._parse_expression)
7152
7153    def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]:
7154        return self._parse_select() or self._parse_set_operations(
7155            self._parse_alias(self._parse_assignment(), explicit=True)
7156            if alias
7157            else self._parse_assignment()
7158        )
7159
7160    def _parse_ddl_select(self) -> t.Optional[exp.Expression]:
7161        return self._parse_query_modifiers(
7162            self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False))
7163        )
7164
7165    def _parse_transaction(self) -> exp.Transaction | exp.Command:
7166        this = None
7167        if self._match_texts(self.TRANSACTION_KIND):
7168            this = self._prev.text
7169
7170        self._match_texts(("TRANSACTION", "WORK"))
7171
7172        modes = []
7173        while True:
7174            mode = []
7175            while self._match(TokenType.VAR):
7176                mode.append(self._prev.text)
7177
7178            if mode:
7179                modes.append(" ".join(mode))
7180            if not self._match(TokenType.COMMA):
7181                break
7182
7183        return self.expression(exp.Transaction, this=this, modes=modes)
7184
7185    def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback:
7186        chain = None
7187        savepoint = None
7188        is_rollback = self._prev.token_type == TokenType.ROLLBACK
7189
7190        self._match_texts(("TRANSACTION", "WORK"))
7191
7192        if self._match_text_seq("TO"):
7193            self._match_text_seq("SAVEPOINT")
7194            savepoint = self._parse_id_var()
7195
7196        if self._match(TokenType.AND):
7197            chain = not self._match_text_seq("NO")
7198            self._match_text_seq("CHAIN")
7199
7200        if is_rollback:
7201            return self.expression(exp.Rollback, savepoint=savepoint)
7202
7203        return self.expression(exp.Commit, chain=chain)
7204
7205    def _parse_refresh(self) -> exp.Refresh:
7206        self._match(TokenType.TABLE)
7207        return self.expression(exp.Refresh, this=self._parse_string() or self._parse_table())
7208
7209    def _parse_add_column(self) -> t.Optional[exp.Expression]:
7210        if not self._match_text_seq("ADD"):
7211            return None
7212
7213        self._match(TokenType.COLUMN)
7214        exists_column = self._parse_exists(not_=True)
7215        expression = self._parse_field_def()
7216
7217        if expression:
7218            expression.set("exists", exists_column)
7219
7220            # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns
7221            if self._match_texts(("FIRST", "AFTER")):
7222                position = self._prev.text
7223                column_position = self.expression(
7224                    exp.ColumnPosition, this=self._parse_column(), position=position
7225                )
7226                expression.set("position", column_position)
7227
7228        return expression
7229
7230    def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]:
7231        drop = self._match(TokenType.DROP) and self._parse_drop()
7232        if drop and not isinstance(drop, exp.Command):
7233            drop.set("kind", drop.args.get("kind", "COLUMN"))
7234        return drop
7235
7236    # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html
7237    def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition:
7238        return self.expression(
7239            exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists
7240        )
7241
7242    def _parse_alter_table_add(self) -> t.List[exp.Expression]:
7243        index = self._index - 1
7244
7245        if self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False):
7246            return self._parse_csv(
7247                lambda: self.expression(
7248                    exp.AddConstraint, expressions=self._parse_csv(self._parse_constraint)
7249                )
7250            )
7251
7252        self._retreat(index)
7253        if not self.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and self._match_text_seq("ADD"):
7254            return self._parse_wrapped_csv(self._parse_field_def, optional=True)
7255
7256        if self._match_text_seq("ADD", "COLUMNS"):
7257            schema = self._parse_schema()
7258            if schema:
7259                return [schema]
7260            return []
7261
7262        return self._parse_wrapped_csv(self._parse_add_column, optional=True)
7263
7264    def _parse_alter_table_alter(self) -> t.Optional[exp.Expression]:
7265        if self._match_texts(self.ALTER_ALTER_PARSERS):
7266            return self.ALTER_ALTER_PARSERS[self._prev.text.upper()](self)
7267
7268        # Many dialects support the ALTER [COLUMN] syntax, so if there is no
7269        # keyword after ALTER we default to parsing this statement
7270        self._match(TokenType.COLUMN)
7271        column = self._parse_field(any_token=True)
7272
7273        if self._match_pair(TokenType.DROP, TokenType.DEFAULT):
7274            return self.expression(exp.AlterColumn, this=column, drop=True)
7275        if self._match_pair(TokenType.SET, TokenType.DEFAULT):
7276            return self.expression(exp.AlterColumn, this=column, default=self._parse_assignment())
7277        if self._match(TokenType.COMMENT):
7278            return self.expression(exp.AlterColumn, this=column, comment=self._parse_string())
7279        if self._match_text_seq("DROP", "NOT", "NULL"):
7280            return self.expression(
7281                exp.AlterColumn,
7282                this=column,
7283                drop=True,
7284                allow_null=True,
7285            )
7286        if self._match_text_seq("SET", "NOT", "NULL"):
7287            return self.expression(
7288                exp.AlterColumn,
7289                this=column,
7290                allow_null=False,
7291            )
7292
7293        if self._match_text_seq("SET", "VISIBLE"):
7294            return self.expression(exp.AlterColumn, this=column, visible="VISIBLE")
7295        if self._match_text_seq("SET", "INVISIBLE"):
7296            return self.expression(exp.AlterColumn, this=column, visible="INVISIBLE")
7297
7298        self._match_text_seq("SET", "DATA")
7299        self._match_text_seq("TYPE")
7300        return self.expression(
7301            exp.AlterColumn,
7302            this=column,
7303            dtype=self._parse_types(),
7304            collate=self._match(TokenType.COLLATE) and self._parse_term(),
7305            using=self._match(TokenType.USING) and self._parse_assignment(),
7306        )
7307
7308    def _parse_alter_diststyle(self) -> exp.AlterDistStyle:
7309        if self._match_texts(("ALL", "EVEN", "AUTO")):
7310            return self.expression(exp.AlterDistStyle, this=exp.var(self._prev.text.upper()))
7311
7312        self._match_text_seq("KEY", "DISTKEY")
7313        return self.expression(exp.AlterDistStyle, this=self._parse_column())
7314
7315    def _parse_alter_sortkey(self, compound: t.Optional[bool] = None) -> exp.AlterSortKey:
7316        if compound:
7317            self._match_text_seq("SORTKEY")
7318
7319        if self._match(TokenType.L_PAREN, advance=False):
7320            return self.expression(
7321                exp.AlterSortKey, expressions=self._parse_wrapped_id_vars(), compound=compound
7322            )
7323
7324        self._match_texts(("AUTO", "NONE"))
7325        return self.expression(
7326            exp.AlterSortKey, this=exp.var(self._prev.text.upper()), compound=compound
7327        )
7328
7329    def _parse_alter_table_drop(self) -> t.List[exp.Expression]:
7330        index = self._index - 1
7331
7332        partition_exists = self._parse_exists()
7333        if self._match(TokenType.PARTITION, advance=False):
7334            return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists))
7335
7336        self._retreat(index)
7337        return self._parse_csv(self._parse_drop_column)
7338
7339    def _parse_alter_table_rename(self) -> t.Optional[exp.AlterRename | exp.RenameColumn]:
7340        if self._match(TokenType.COLUMN):
7341            exists = self._parse_exists()
7342            old_column = self._parse_column()
7343            to = self._match_text_seq("TO")
7344            new_column = self._parse_column()
7345
7346            if old_column is None or to is None or new_column is None:
7347                return None
7348
7349            return self.expression(exp.RenameColumn, this=old_column, to=new_column, exists=exists)
7350
7351        self._match_text_seq("TO")
7352        return self.expression(exp.AlterRename, this=self._parse_table(schema=True))
7353
7354    def _parse_alter_table_set(self) -> exp.AlterSet:
7355        alter_set = self.expression(exp.AlterSet)
7356
7357        if self._match(TokenType.L_PAREN, advance=False) or self._match_text_seq(
7358            "TABLE", "PROPERTIES"
7359        ):
7360            alter_set.set("expressions", self._parse_wrapped_csv(self._parse_assignment))
7361        elif self._match_text_seq("FILESTREAM_ON", advance=False):
7362            alter_set.set("expressions", [self._parse_assignment()])
7363        elif self._match_texts(("LOGGED", "UNLOGGED")):
7364            alter_set.set("option", exp.var(self._prev.text.upper()))
7365        elif self._match_text_seq("WITHOUT") and self._match_texts(("CLUSTER", "OIDS")):
7366            alter_set.set("option", exp.var(f"WITHOUT {self._prev.text.upper()}"))
7367        elif self._match_text_seq("LOCATION"):
7368            alter_set.set("location", self._parse_field())
7369        elif self._match_text_seq("ACCESS", "METHOD"):
7370            alter_set.set("access_method", self._parse_field())
7371        elif self._match_text_seq("TABLESPACE"):
7372            alter_set.set("tablespace", self._parse_field())
7373        elif self._match_text_seq("FILE", "FORMAT") or self._match_text_seq("FILEFORMAT"):
7374            alter_set.set("file_format", [self._parse_field()])
7375        elif self._match_text_seq("STAGE_FILE_FORMAT"):
7376            alter_set.set("file_format", self._parse_wrapped_options())
7377        elif self._match_text_seq("STAGE_COPY_OPTIONS"):
7378            alter_set.set("copy_options", self._parse_wrapped_options())
7379        elif self._match_text_seq("TAG") or self._match_text_seq("TAGS"):
7380            alter_set.set("tag", self._parse_csv(self._parse_assignment))
7381        else:
7382            if self._match_text_seq("SERDE"):
7383                alter_set.set("serde", self._parse_field())
7384
7385            alter_set.set("expressions", [self._parse_properties()])
7386
7387        return alter_set
7388
7389    def _parse_alter(self) -> exp.Alter | exp.Command:
7390        start = self._prev
7391
7392        alter_token = self._match_set(self.ALTERABLES) and self._prev
7393        if not alter_token:
7394            return self._parse_as_command(start)
7395
7396        exists = self._parse_exists()
7397        only = self._match_text_seq("ONLY")
7398        this = self._parse_table(schema=True)
7399        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
7400
7401        if self._next:
7402            self._advance()
7403
7404        parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None
7405        if parser:
7406            actions = ensure_list(parser(self))
7407            not_valid = self._match_text_seq("NOT", "VALID")
7408            options = self._parse_csv(self._parse_property)
7409
7410            if not self._curr and actions:
7411                return self.expression(
7412                    exp.Alter,
7413                    this=this,
7414                    kind=alter_token.text.upper(),
7415                    exists=exists,
7416                    actions=actions,
7417                    only=only,
7418                    options=options,
7419                    cluster=cluster,
7420                    not_valid=not_valid,
7421                )
7422
7423        return self._parse_as_command(start)
7424
7425    def _parse_analyze(self) -> exp.Analyze | exp.Command:
7426        start = self._prev
7427        # https://duckdb.org/docs/sql/statements/analyze
7428        if not self._curr:
7429            return self.expression(exp.Analyze)
7430
7431        options = []
7432        while self._match_texts(self.ANALYZE_STYLES):
7433            if self._prev.text.upper() == "BUFFER_USAGE_LIMIT":
7434                options.append(f"BUFFER_USAGE_LIMIT {self._parse_number()}")
7435            else:
7436                options.append(self._prev.text.upper())
7437
7438        this: t.Optional[exp.Expression] = None
7439        inner_expression: t.Optional[exp.Expression] = None
7440
7441        kind = self._curr and self._curr.text.upper()
7442
7443        if self._match(TokenType.TABLE) or self._match(TokenType.INDEX):
7444            this = self._parse_table_parts()
7445        elif self._match_text_seq("TABLES"):
7446            if self._match_set((TokenType.FROM, TokenType.IN)):
7447                kind = f"{kind} {self._prev.text.upper()}"
7448                this = self._parse_table(schema=True, is_db_reference=True)
7449        elif self._match_text_seq("DATABASE"):
7450            this = self._parse_table(schema=True, is_db_reference=True)
7451        elif self._match_text_seq("CLUSTER"):
7452            this = self._parse_table()
7453        # Try matching inner expr keywords before fallback to parse table.
7454        elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7455            kind = None
7456            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7457        else:
7458            # Empty kind  https://prestodb.io/docs/current/sql/analyze.html
7459            kind = None
7460            this = self._parse_table_parts()
7461
7462        partition = self._try_parse(self._parse_partition)
7463        if not partition and self._match_texts(self.PARTITION_KEYWORDS):
7464            return self._parse_as_command(start)
7465
7466        # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7467        if self._match_text_seq("WITH", "SYNC", "MODE") or self._match_text_seq(
7468            "WITH", "ASYNC", "MODE"
7469        ):
7470            mode = f"WITH {self._tokens[self._index - 2].text.upper()} MODE"
7471        else:
7472            mode = None
7473
7474        if self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7475            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7476
7477        properties = self._parse_properties()
7478        return self.expression(
7479            exp.Analyze,
7480            kind=kind,
7481            this=this,
7482            mode=mode,
7483            partition=partition,
7484            properties=properties,
7485            expression=inner_expression,
7486            options=options,
7487        )
7488
7489    # https://spark.apache.org/docs/3.5.1/sql-ref-syntax-aux-analyze-table.html
7490    def _parse_analyze_statistics(self) -> exp.AnalyzeStatistics:
7491        this = None
7492        kind = self._prev.text.upper()
7493        option = self._prev.text.upper() if self._match_text_seq("DELTA") else None
7494        expressions = []
7495
7496        if not self._match_text_seq("STATISTICS"):
7497            self.raise_error("Expecting token STATISTICS")
7498
7499        if self._match_text_seq("NOSCAN"):
7500            this = "NOSCAN"
7501        elif self._match(TokenType.FOR):
7502            if self._match_text_seq("ALL", "COLUMNS"):
7503                this = "FOR ALL COLUMNS"
7504            if self._match_texts("COLUMNS"):
7505                this = "FOR COLUMNS"
7506                expressions = self._parse_csv(self._parse_column_reference)
7507        elif self._match_text_seq("SAMPLE"):
7508            sample = self._parse_number()
7509            expressions = [
7510                self.expression(
7511                    exp.AnalyzeSample,
7512                    sample=sample,
7513                    kind=self._prev.text.upper() if self._match(TokenType.PERCENT) else None,
7514                )
7515            ]
7516
7517        return self.expression(
7518            exp.AnalyzeStatistics, kind=kind, option=option, this=this, expressions=expressions
7519        )
7520
7521    # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ANALYZE.html
7522    def _parse_analyze_validate(self) -> exp.AnalyzeValidate:
7523        kind = None
7524        this = None
7525        expression: t.Optional[exp.Expression] = None
7526        if self._match_text_seq("REF", "UPDATE"):
7527            kind = "REF"
7528            this = "UPDATE"
7529            if self._match_text_seq("SET", "DANGLING", "TO", "NULL"):
7530                this = "UPDATE SET DANGLING TO NULL"
7531        elif self._match_text_seq("STRUCTURE"):
7532            kind = "STRUCTURE"
7533            if self._match_text_seq("CASCADE", "FAST"):
7534                this = "CASCADE FAST"
7535            elif self._match_text_seq("CASCADE", "COMPLETE") and self._match_texts(
7536                ("ONLINE", "OFFLINE")
7537            ):
7538                this = f"CASCADE COMPLETE {self._prev.text.upper()}"
7539                expression = self._parse_into()
7540
7541        return self.expression(exp.AnalyzeValidate, kind=kind, this=this, expression=expression)
7542
7543    def _parse_analyze_columns(self) -> t.Optional[exp.AnalyzeColumns]:
7544        this = self._prev.text.upper()
7545        if self._match_text_seq("COLUMNS"):
7546            return self.expression(exp.AnalyzeColumns, this=f"{this} {self._prev.text.upper()}")
7547        return None
7548
7549    def _parse_analyze_delete(self) -> t.Optional[exp.AnalyzeDelete]:
7550        kind = self._prev.text.upper() if self._match_text_seq("SYSTEM") else None
7551        if self._match_text_seq("STATISTICS"):
7552            return self.expression(exp.AnalyzeDelete, kind=kind)
7553        return None
7554
7555    def _parse_analyze_list(self) -> t.Optional[exp.AnalyzeListChainedRows]:
7556        if self._match_text_seq("CHAINED", "ROWS"):
7557            return self.expression(exp.AnalyzeListChainedRows, expression=self._parse_into())
7558        return None
7559
7560    # https://dev.mysql.com/doc/refman/8.4/en/analyze-table.html
7561    def _parse_analyze_histogram(self) -> exp.AnalyzeHistogram:
7562        this = self._prev.text.upper()
7563        expression: t.Optional[exp.Expression] = None
7564        expressions = []
7565        update_options = None
7566
7567        if self._match_text_seq("HISTOGRAM", "ON"):
7568            expressions = self._parse_csv(self._parse_column_reference)
7569            with_expressions = []
7570            while self._match(TokenType.WITH):
7571                # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7572                if self._match_texts(("SYNC", "ASYNC")):
7573                    if self._match_text_seq("MODE", advance=False):
7574                        with_expressions.append(f"{self._prev.text.upper()} MODE")
7575                        self._advance()
7576                else:
7577                    buckets = self._parse_number()
7578                    if self._match_text_seq("BUCKETS"):
7579                        with_expressions.append(f"{buckets} BUCKETS")
7580            if with_expressions:
7581                expression = self.expression(exp.AnalyzeWith, expressions=with_expressions)
7582
7583            if self._match_texts(("MANUAL", "AUTO")) and self._match(
7584                TokenType.UPDATE, advance=False
7585            ):
7586                update_options = self._prev.text.upper()
7587                self._advance()
7588            elif self._match_text_seq("USING", "DATA"):
7589                expression = self.expression(exp.UsingData, this=self._parse_string())
7590
7591        return self.expression(
7592            exp.AnalyzeHistogram,
7593            this=this,
7594            expressions=expressions,
7595            expression=expression,
7596            update_options=update_options,
7597        )
7598
7599    def _parse_merge(self) -> exp.Merge:
7600        self._match(TokenType.INTO)
7601        target = self._parse_table()
7602
7603        if target and self._match(TokenType.ALIAS, advance=False):
7604            target.set("alias", self._parse_table_alias())
7605
7606        self._match(TokenType.USING)
7607        using = self._parse_table()
7608
7609        self._match(TokenType.ON)
7610        on = self._parse_assignment()
7611
7612        return self.expression(
7613            exp.Merge,
7614            this=target,
7615            using=using,
7616            on=on,
7617            whens=self._parse_when_matched(),
7618            returning=self._parse_returning(),
7619        )
7620
7621    def _parse_when_matched(self) -> exp.Whens:
7622        whens = []
7623
7624        while self._match(TokenType.WHEN):
7625            matched = not self._match(TokenType.NOT)
7626            self._match_text_seq("MATCHED")
7627            source = (
7628                False
7629                if self._match_text_seq("BY", "TARGET")
7630                else self._match_text_seq("BY", "SOURCE")
7631            )
7632            condition = self._parse_assignment() if self._match(TokenType.AND) else None
7633
7634            self._match(TokenType.THEN)
7635
7636            if self._match(TokenType.INSERT):
7637                this = self._parse_star()
7638                if this:
7639                    then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=this)
7640                else:
7641                    then = self.expression(
7642                        exp.Insert,
7643                        this=exp.var("ROW")
7644                        if self._match_text_seq("ROW")
7645                        else self._parse_value(values=False),
7646                        expression=self._match_text_seq("VALUES") and self._parse_value(),
7647                    )
7648            elif self._match(TokenType.UPDATE):
7649                expressions = self._parse_star()
7650                if expressions:
7651                    then = self.expression(exp.Update, expressions=expressions)
7652                else:
7653                    then = self.expression(
7654                        exp.Update,
7655                        expressions=self._match(TokenType.SET)
7656                        and self._parse_csv(self._parse_equality),
7657                    )
7658            elif self._match(TokenType.DELETE):
7659                then = self.expression(exp.Var, this=self._prev.text)
7660            else:
7661                then = self._parse_var_from_options(self.CONFLICT_ACTIONS)
7662
7663            whens.append(
7664                self.expression(
7665                    exp.When,
7666                    matched=matched,
7667                    source=source,
7668                    condition=condition,
7669                    then=then,
7670                )
7671            )
7672        return self.expression(exp.Whens, expressions=whens)
7673
7674    def _parse_show(self) -> t.Optional[exp.Expression]:
7675        parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE)
7676        if parser:
7677            return parser(self)
7678        return self._parse_as_command(self._prev)
7679
7680    def _parse_set_item_assignment(
7681        self, kind: t.Optional[str] = None
7682    ) -> t.Optional[exp.Expression]:
7683        index = self._index
7684
7685        if kind in ("GLOBAL", "SESSION") and self._match_text_seq("TRANSACTION"):
7686            return self._parse_set_transaction(global_=kind == "GLOBAL")
7687
7688        left = self._parse_primary() or self._parse_column()
7689        assignment_delimiter = self._match_texts(("=", "TO"))
7690
7691        if not left or (self.SET_REQUIRES_ASSIGNMENT_DELIMITER and not assignment_delimiter):
7692            self._retreat(index)
7693            return None
7694
7695        right = self._parse_statement() or self._parse_id_var()
7696        if isinstance(right, (exp.Column, exp.Identifier)):
7697            right = exp.var(right.name)
7698
7699        this = self.expression(exp.EQ, this=left, expression=right)
7700        return self.expression(exp.SetItem, this=this, kind=kind)
7701
7702    def _parse_set_transaction(self, global_: bool = False) -> exp.Expression:
7703        self._match_text_seq("TRANSACTION")
7704        characteristics = self._parse_csv(
7705            lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS)
7706        )
7707        return self.expression(
7708            exp.SetItem,
7709            expressions=characteristics,
7710            kind="TRANSACTION",
7711            **{"global": global_},  # type: ignore
7712        )
7713
7714    def _parse_set_item(self) -> t.Optional[exp.Expression]:
7715        parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE)
7716        return parser(self) if parser else self._parse_set_item_assignment(kind=None)
7717
7718    def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command:
7719        index = self._index
7720        set_ = self.expression(
7721            exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag
7722        )
7723
7724        if self._curr:
7725            self._retreat(index)
7726            return self._parse_as_command(self._prev)
7727
7728        return set_
7729
7730    def _parse_var_from_options(
7731        self, options: OPTIONS_TYPE, raise_unmatched: bool = True
7732    ) -> t.Optional[exp.Var]:
7733        start = self._curr
7734        if not start:
7735            return None
7736
7737        option = start.text.upper()
7738        continuations = options.get(option)
7739
7740        index = self._index
7741        self._advance()
7742        for keywords in continuations or []:
7743            if isinstance(keywords, str):
7744                keywords = (keywords,)
7745
7746            if self._match_text_seq(*keywords):
7747                option = f"{option} {' '.join(keywords)}"
7748                break
7749        else:
7750            if continuations or continuations is None:
7751                if raise_unmatched:
7752                    self.raise_error(f"Unknown option {option}")
7753
7754                self._retreat(index)
7755                return None
7756
7757        return exp.var(option)
7758
7759    def _parse_as_command(self, start: Token) -> exp.Command:
7760        while self._curr:
7761            self._advance()
7762        text = self._find_sql(start, self._prev)
7763        size = len(start.text)
7764        self._warn_unsupported()
7765        return exp.Command(this=text[:size], expression=text[size:])
7766
7767    def _parse_dict_property(self, this: str) -> exp.DictProperty:
7768        settings = []
7769
7770        self._match_l_paren()
7771        kind = self._parse_id_var()
7772
7773        if self._match(TokenType.L_PAREN):
7774            while True:
7775                key = self._parse_id_var()
7776                value = self._parse_primary()
7777                if not key and value is None:
7778                    break
7779                settings.append(self.expression(exp.DictSubProperty, this=key, value=value))
7780            self._match(TokenType.R_PAREN)
7781
7782        self._match_r_paren()
7783
7784        return self.expression(
7785            exp.DictProperty,
7786            this=this,
7787            kind=kind.this if kind else None,
7788            settings=settings,
7789        )
7790
7791    def _parse_dict_range(self, this: str) -> exp.DictRange:
7792        self._match_l_paren()
7793        has_min = self._match_text_seq("MIN")
7794        if has_min:
7795            min = self._parse_var() or self._parse_primary()
7796            self._match_text_seq("MAX")
7797            max = self._parse_var() or self._parse_primary()
7798        else:
7799            max = self._parse_var() or self._parse_primary()
7800            min = exp.Literal.number(0)
7801        self._match_r_paren()
7802        return self.expression(exp.DictRange, this=this, min=min, max=max)
7803
7804    def _parse_comprehension(
7805        self, this: t.Optional[exp.Expression]
7806    ) -> t.Optional[exp.Comprehension]:
7807        index = self._index
7808        expression = self._parse_column()
7809        if not self._match(TokenType.IN):
7810            self._retreat(index - 1)
7811            return None
7812        iterator = self._parse_column()
7813        condition = self._parse_assignment() if self._match_text_seq("IF") else None
7814        return self.expression(
7815            exp.Comprehension,
7816            this=this,
7817            expression=expression,
7818            iterator=iterator,
7819            condition=condition,
7820        )
7821
7822    def _parse_heredoc(self) -> t.Optional[exp.Heredoc]:
7823        if self._match(TokenType.HEREDOC_STRING):
7824            return self.expression(exp.Heredoc, this=self._prev.text)
7825
7826        if not self._match_text_seq("$"):
7827            return None
7828
7829        tags = ["$"]
7830        tag_text = None
7831
7832        if self._is_connected():
7833            self._advance()
7834            tags.append(self._prev.text.upper())
7835        else:
7836            self.raise_error("No closing $ found")
7837
7838        if tags[-1] != "$":
7839            if self._is_connected() and self._match_text_seq("$"):
7840                tag_text = tags[-1]
7841                tags.append("$")
7842            else:
7843                self.raise_error("No closing $ found")
7844
7845        heredoc_start = self._curr
7846
7847        while self._curr:
7848            if self._match_text_seq(*tags, advance=False):
7849                this = self._find_sql(heredoc_start, self._prev)
7850                self._advance(len(tags))
7851                return self.expression(exp.Heredoc, this=this, tag=tag_text)
7852
7853            self._advance()
7854
7855        self.raise_error(f"No closing {''.join(tags)} found")
7856        return None
7857
7858    def _find_parser(
7859        self, parsers: t.Dict[str, t.Callable], trie: t.Dict
7860    ) -> t.Optional[t.Callable]:
7861        if not self._curr:
7862            return None
7863
7864        index = self._index
7865        this = []
7866        while True:
7867            # The current token might be multiple words
7868            curr = self._curr.text.upper()
7869            key = curr.split(" ")
7870            this.append(curr)
7871
7872            self._advance()
7873            result, trie = in_trie(trie, key)
7874            if result == TrieResult.FAILED:
7875                break
7876
7877            if result == TrieResult.EXISTS:
7878                subparser = parsers[" ".join(this)]
7879                return subparser
7880
7881        self._retreat(index)
7882        return None
7883
7884    def _match(self, token_type, advance=True, expression=None):
7885        if not self._curr:
7886            return None
7887
7888        if self._curr.token_type == token_type:
7889            if advance:
7890                self._advance()
7891            self._add_comments(expression)
7892            return True
7893
7894        return None
7895
7896    def _match_set(self, types, advance=True):
7897        if not self._curr:
7898            return None
7899
7900        if self._curr.token_type in types:
7901            if advance:
7902                self._advance()
7903            return True
7904
7905        return None
7906
7907    def _match_pair(self, token_type_a, token_type_b, advance=True):
7908        if not self._curr or not self._next:
7909            return None
7910
7911        if self._curr.token_type == token_type_a and self._next.token_type == token_type_b:
7912            if advance:
7913                self._advance(2)
7914            return True
7915
7916        return None
7917
7918    def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
7919        if not self._match(TokenType.L_PAREN, expression=expression):
7920            self.raise_error("Expecting (")
7921
7922    def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
7923        if not self._match(TokenType.R_PAREN, expression=expression):
7924            self.raise_error("Expecting )")
7925
7926    def _match_texts(self, texts, advance=True):
7927        if (
7928            self._curr
7929            and self._curr.token_type != TokenType.STRING
7930            and self._curr.text.upper() in texts
7931        ):
7932            if advance:
7933                self._advance()
7934            return True
7935        return None
7936
7937    def _match_text_seq(self, *texts, advance=True):
7938        index = self._index
7939        for text in texts:
7940            if (
7941                self._curr
7942                and self._curr.token_type != TokenType.STRING
7943                and self._curr.text.upper() == text
7944            ):
7945                self._advance()
7946            else:
7947                self._retreat(index)
7948                return None
7949
7950        if not advance:
7951            self._retreat(index)
7952
7953        return True
7954
7955    def _replace_lambda(
7956        self, node: t.Optional[exp.Expression], expressions: t.List[exp.Expression]
7957    ) -> t.Optional[exp.Expression]:
7958        if not node:
7959            return node
7960
7961        lambda_types = {e.name: e.args.get("to") or False for e in expressions}
7962
7963        for column in node.find_all(exp.Column):
7964            typ = lambda_types.get(column.parts[0].name)
7965            if typ is not None:
7966                dot_or_id = column.to_dot() if column.table else column.this
7967
7968                if typ:
7969                    dot_or_id = self.expression(
7970                        exp.Cast,
7971                        this=dot_or_id,
7972                        to=typ,
7973                    )
7974
7975                parent = column.parent
7976
7977                while isinstance(parent, exp.Dot):
7978                    if not isinstance(parent.parent, exp.Dot):
7979                        parent.replace(dot_or_id)
7980                        break
7981                    parent = parent.parent
7982                else:
7983                    if column is node:
7984                        node = dot_or_id
7985                    else:
7986                        column.replace(dot_or_id)
7987        return node
7988
7989    def _parse_truncate_table(self) -> t.Optional[exp.TruncateTable] | exp.Expression:
7990        start = self._prev
7991
7992        # Not to be confused with TRUNCATE(number, decimals) function call
7993        if self._match(TokenType.L_PAREN):
7994            self._retreat(self._index - 2)
7995            return self._parse_function()
7996
7997        # Clickhouse supports TRUNCATE DATABASE as well
7998        is_database = self._match(TokenType.DATABASE)
7999
8000        self._match(TokenType.TABLE)
8001
8002        exists = self._parse_exists(not_=False)
8003
8004        expressions = self._parse_csv(
8005            lambda: self._parse_table(schema=True, is_db_reference=is_database)
8006        )
8007
8008        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
8009
8010        if self._match_text_seq("RESTART", "IDENTITY"):
8011            identity = "RESTART"
8012        elif self._match_text_seq("CONTINUE", "IDENTITY"):
8013            identity = "CONTINUE"
8014        else:
8015            identity = None
8016
8017        if self._match_text_seq("CASCADE") or self._match_text_seq("RESTRICT"):
8018            option = self._prev.text
8019        else:
8020            option = None
8021
8022        partition = self._parse_partition()
8023
8024        # Fallback case
8025        if self._curr:
8026            return self._parse_as_command(start)
8027
8028        return self.expression(
8029            exp.TruncateTable,
8030            expressions=expressions,
8031            is_database=is_database,
8032            exists=exists,
8033            cluster=cluster,
8034            identity=identity,
8035            option=option,
8036            partition=partition,
8037        )
8038
8039    def _parse_with_operator(self) -> t.Optional[exp.Expression]:
8040        this = self._parse_ordered(self._parse_opclass)
8041
8042        if not self._match(TokenType.WITH):
8043            return this
8044
8045        op = self._parse_var(any_token=True)
8046
8047        return self.expression(exp.WithOperator, this=this, op=op)
8048
8049    def _parse_wrapped_options(self) -> t.List[t.Optional[exp.Expression]]:
8050        self._match(TokenType.EQ)
8051        self._match(TokenType.L_PAREN)
8052
8053        opts: t.List[t.Optional[exp.Expression]] = []
8054        option: exp.Expression | None
8055        while self._curr and not self._match(TokenType.R_PAREN):
8056            if self._match_text_seq("FORMAT_NAME", "="):
8057                # The FORMAT_NAME can be set to an identifier for Snowflake and T-SQL
8058                option = self._parse_format_name()
8059            else:
8060                option = self._parse_property()
8061
8062            if option is None:
8063                self.raise_error("Unable to parse option")
8064                break
8065
8066            opts.append(option)
8067
8068        return opts
8069
8070    def _parse_copy_parameters(self) -> t.List[exp.CopyParameter]:
8071        sep = TokenType.COMMA if self.dialect.COPY_PARAMS_ARE_CSV else None
8072
8073        options = []
8074        while self._curr and not self._match(TokenType.R_PAREN, advance=False):
8075            option = self._parse_var(any_token=True)
8076            prev = self._prev.text.upper()
8077
8078            # Different dialects might separate options and values by white space, "=" and "AS"
8079            self._match(TokenType.EQ)
8080            self._match(TokenType.ALIAS)
8081
8082            param = self.expression(exp.CopyParameter, this=option)
8083
8084            if prev in self.COPY_INTO_VARLEN_OPTIONS and self._match(
8085                TokenType.L_PAREN, advance=False
8086            ):
8087                # Snowflake FILE_FORMAT case, Databricks COPY & FORMAT options
8088                param.set("expressions", self._parse_wrapped_options())
8089            elif prev == "FILE_FORMAT":
8090                # T-SQL's external file format case
8091                param.set("expression", self._parse_field())
8092            else:
8093                param.set("expression", self._parse_unquoted_field())
8094
8095            options.append(param)
8096            self._match(sep)
8097
8098        return options
8099
8100    def _parse_credentials(self) -> t.Optional[exp.Credentials]:
8101        expr = self.expression(exp.Credentials)
8102
8103        if self._match_text_seq("STORAGE_INTEGRATION", "="):
8104            expr.set("storage", self._parse_field())
8105        if self._match_text_seq("CREDENTIALS"):
8106            # Snowflake case: CREDENTIALS = (...), Redshift case: CREDENTIALS <string>
8107            creds = (
8108                self._parse_wrapped_options() if self._match(TokenType.EQ) else self._parse_field()
8109            )
8110            expr.set("credentials", creds)
8111        if self._match_text_seq("ENCRYPTION"):
8112            expr.set("encryption", self._parse_wrapped_options())
8113        if self._match_text_seq("IAM_ROLE"):
8114            expr.set("iam_role", self._parse_field())
8115        if self._match_text_seq("REGION"):
8116            expr.set("region", self._parse_field())
8117
8118        return expr
8119
8120    def _parse_file_location(self) -> t.Optional[exp.Expression]:
8121        return self._parse_field()
8122
8123    def _parse_copy(self) -> exp.Copy | exp.Command:
8124        start = self._prev
8125
8126        self._match(TokenType.INTO)
8127
8128        this = (
8129            self._parse_select(nested=True, parse_subquery_alias=False)
8130            if self._match(TokenType.L_PAREN, advance=False)
8131            else self._parse_table(schema=True)
8132        )
8133
8134        kind = self._match(TokenType.FROM) or not self._match_text_seq("TO")
8135
8136        files = self._parse_csv(self._parse_file_location)
8137        credentials = self._parse_credentials()
8138
8139        self._match_text_seq("WITH")
8140
8141        params = self._parse_wrapped(self._parse_copy_parameters, optional=True)
8142
8143        # Fallback case
8144        if self._curr:
8145            return self._parse_as_command(start)
8146
8147        return self.expression(
8148            exp.Copy,
8149            this=this,
8150            kind=kind,
8151            credentials=credentials,
8152            files=files,
8153            params=params,
8154        )
8155
8156    def _parse_normalize(self) -> exp.Normalize:
8157        return self.expression(
8158            exp.Normalize,
8159            this=self._parse_bitwise(),
8160            form=self._match(TokenType.COMMA) and self._parse_var(),
8161        )
8162
8163    def _parse_ceil_floor(self, expr_type: t.Type[TCeilFloor]) -> TCeilFloor:
8164        args = self._parse_csv(lambda: self._parse_lambda())
8165
8166        this = seq_get(args, 0)
8167        decimals = seq_get(args, 1)
8168
8169        return expr_type(
8170            this=this, decimals=decimals, to=self._match_text_seq("TO") and self._parse_var()
8171        )
8172
8173    def _parse_star_ops(self) -> t.Optional[exp.Expression]:
8174        if self._match_text_seq("COLUMNS", "(", advance=False):
8175            this = self._parse_function()
8176            if isinstance(this, exp.Columns):
8177                this.set("unpack", True)
8178            return this
8179
8180        return self.expression(
8181            exp.Star,
8182            **{  # type: ignore
8183                "except": self._parse_star_op("EXCEPT", "EXCLUDE"),
8184                "replace": self._parse_star_op("REPLACE"),
8185                "rename": self._parse_star_op("RENAME"),
8186            },
8187        )
8188
8189    def _parse_grant_privilege(self) -> t.Optional[exp.GrantPrivilege]:
8190        privilege_parts = []
8191
8192        # Keep consuming consecutive keywords until comma (end of this privilege) or ON
8193        # (end of privilege list) or L_PAREN (start of column list) are met
8194        while self._curr and not self._match_set(self.PRIVILEGE_FOLLOW_TOKENS, advance=False):
8195            privilege_parts.append(self._curr.text.upper())
8196            self._advance()
8197
8198        this = exp.var(" ".join(privilege_parts))
8199        expressions = (
8200            self._parse_wrapped_csv(self._parse_column)
8201            if self._match(TokenType.L_PAREN, advance=False)
8202            else None
8203        )
8204
8205        return self.expression(exp.GrantPrivilege, this=this, expressions=expressions)
8206
8207    def _parse_grant_principal(self) -> t.Optional[exp.GrantPrincipal]:
8208        kind = self._match_texts(("ROLE", "GROUP")) and self._prev.text.upper()
8209        principal = self._parse_id_var()
8210
8211        if not principal:
8212            return None
8213
8214        return self.expression(exp.GrantPrincipal, this=principal, kind=kind)
8215
8216    def _parse_grant(self) -> exp.Grant | exp.Command:
8217        start = self._prev
8218
8219        privileges = self._parse_csv(self._parse_grant_privilege)
8220
8221        self._match(TokenType.ON)
8222        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
8223
8224        # Attempt to parse the securable e.g. MySQL allows names
8225        # such as "foo.*", "*.*" which are not easily parseable yet
8226        securable = self._try_parse(self._parse_table_parts)
8227
8228        if not securable or not self._match_text_seq("TO"):
8229            return self._parse_as_command(start)
8230
8231        principals = self._parse_csv(self._parse_grant_principal)
8232
8233        grant_option = self._match_text_seq("WITH", "GRANT", "OPTION")
8234
8235        if self._curr:
8236            return self._parse_as_command(start)
8237
8238        return self.expression(
8239            exp.Grant,
8240            privileges=privileges,
8241            kind=kind,
8242            securable=securable,
8243            principals=principals,
8244            grant_option=grant_option,
8245        )
8246
8247    def _parse_overlay(self) -> exp.Overlay:
8248        return self.expression(
8249            exp.Overlay,
8250            **{  # type: ignore
8251                "this": self._parse_bitwise(),
8252                "expression": self._match_text_seq("PLACING") and self._parse_bitwise(),
8253                "from": self._match_text_seq("FROM") and self._parse_bitwise(),
8254                "for": self._match_text_seq("FOR") and self._parse_bitwise(),
8255            },
8256        )
8257
8258    def _parse_format_name(self) -> exp.Property:
8259        # Note: Although not specified in the docs, Snowflake does accept a string/identifier
8260        # for FILE_FORMAT = <format_name>
8261        return self.expression(
8262            exp.Property,
8263            this=exp.var("FORMAT_NAME"),
8264            value=self._parse_string() or self._parse_table_parts(),
8265        )
8266
8267    def _parse_max_min_by(self, expr_type: t.Type[exp.AggFunc]) -> exp.AggFunc:
8268        args: t.List[exp.Expression] = []
8269
8270        if self._match(TokenType.DISTINCT):
8271            args.append(self.expression(exp.Distinct, expressions=[self._parse_assignment()]))
8272            self._match(TokenType.COMMA)
8273
8274        args.extend(self._parse_csv(self._parse_assignment))
8275
8276        return self.expression(
8277            expr_type, this=seq_get(args, 0), expression=seq_get(args, 1), count=seq_get(args, 2)
8278        )
8279
8280    def _identifier_expression(
8281        self, token: t.Optional[Token] = None, **kwargs: t.Any
8282    ) -> exp.Identifier:
8283        token = token or self._prev
8284        expression = self.expression(exp.Identifier, this=token.text, **kwargs)
8285        expression.update_positions(token)
8286        return expression
logger = <Logger sqlglot (WARNING)>
OPTIONS_TYPE = typing.Dict[str, typing.Sequence[typing.Union[typing.Sequence[str], str]]]
def build_var_map(args: List) -> sqlglot.expressions.StarMap | sqlglot.expressions.VarMap:
28def build_var_map(args: t.List) -> exp.StarMap | exp.VarMap:
29    if len(args) == 1 and args[0].is_star:
30        return exp.StarMap(this=args[0])
31
32    keys = []
33    values = []
34    for i in range(0, len(args), 2):
35        keys.append(args[i])
36        values.append(args[i + 1])
37
38    return exp.VarMap(keys=exp.array(*keys, copy=False), values=exp.array(*values, copy=False))
def build_like(args: List) -> sqlglot.expressions.Escape | sqlglot.expressions.Like:
41def build_like(args: t.List) -> exp.Escape | exp.Like:
42    like = exp.Like(this=seq_get(args, 1), expression=seq_get(args, 0))
43    return exp.Escape(this=like, expression=seq_get(args, 2)) if len(args) > 2 else like
def binary_range_parser( expr_type: Type[sqlglot.expressions.Expression], reverse_args: bool = False) -> Callable[[Parser, Optional[sqlglot.expressions.Expression]], Optional[sqlglot.expressions.Expression]]:
46def binary_range_parser(
47    expr_type: t.Type[exp.Expression], reverse_args: bool = False
48) -> t.Callable[[Parser, t.Optional[exp.Expression]], t.Optional[exp.Expression]]:
49    def _parse_binary_range(
50        self: Parser, this: t.Optional[exp.Expression]
51    ) -> t.Optional[exp.Expression]:
52        expression = self._parse_bitwise()
53        if reverse_args:
54            this, expression = expression, this
55        return self._parse_escape(self.expression(expr_type, this=this, expression=expression))
56
57    return _parse_binary_range
def build_logarithm( args: List, dialect: sqlglot.dialects.Dialect) -> sqlglot.expressions.Func:
60def build_logarithm(args: t.List, dialect: Dialect) -> exp.Func:
61    # Default argument order is base, expression
62    this = seq_get(args, 0)
63    expression = seq_get(args, 1)
64
65    if expression:
66        if not dialect.LOG_BASE_FIRST:
67            this, expression = expression, this
68        return exp.Log(this=this, expression=expression)
69
70    return (exp.Ln if dialect.parser_class.LOG_DEFAULTS_TO_LN else exp.Log)(this=this)
def build_hex( args: List, dialect: sqlglot.dialects.Dialect) -> sqlglot.expressions.Hex | sqlglot.expressions.LowerHex:
73def build_hex(args: t.List, dialect: Dialect) -> exp.Hex | exp.LowerHex:
74    arg = seq_get(args, 0)
75    return exp.LowerHex(this=arg) if dialect.HEX_LOWERCASE else exp.Hex(this=arg)
def build_lower(args: List) -> sqlglot.expressions.Lower | sqlglot.expressions.Hex:
78def build_lower(args: t.List) -> exp.Lower | exp.Hex:
79    # LOWER(HEX(..)) can be simplified to LowerHex to simplify its transpilation
80    arg = seq_get(args, 0)
81    return exp.LowerHex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Lower(this=arg)
def build_upper(args: List) -> sqlglot.expressions.Upper | sqlglot.expressions.Hex:
84def build_upper(args: t.List) -> exp.Upper | exp.Hex:
85    # UPPER(HEX(..)) can be simplified to Hex to simplify its transpilation
86    arg = seq_get(args, 0)
87    return exp.Hex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Upper(this=arg)
def build_extract_json_with_path( expr_type: Type[~E]) -> Callable[[List, sqlglot.dialects.Dialect], ~E]:
 90def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
 91    def _builder(args: t.List, dialect: Dialect) -> E:
 92        expression = expr_type(
 93            this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
 94        )
 95        if len(args) > 2 and expr_type is exp.JSONExtract:
 96            expression.set("expressions", args[2:])
 97
 98        return expression
 99
100    return _builder
def build_mod(args: List) -> sqlglot.expressions.Mod:
103def build_mod(args: t.List) -> exp.Mod:
104    this = seq_get(args, 0)
105    expression = seq_get(args, 1)
106
107    # Wrap the operands if they are binary nodes, e.g. MOD(a + 1, 7) -> (a + 1) % 7
108    this = exp.Paren(this=this) if isinstance(this, exp.Binary) else this
109    expression = exp.Paren(this=expression) if isinstance(expression, exp.Binary) else expression
110
111    return exp.Mod(this=this, expression=expression)
def build_pad(args: List, is_left: bool = True):
114def build_pad(args: t.List, is_left: bool = True):
115    return exp.Pad(
116        this=seq_get(args, 0),
117        expression=seq_get(args, 1),
118        fill_pattern=seq_get(args, 2),
119        is_left=is_left,
120    )
def build_array_constructor( exp_class: Type[~E], args: List, bracket_kind: sqlglot.tokens.TokenType, dialect: sqlglot.dialects.Dialect) -> sqlglot.expressions.Expression:
123def build_array_constructor(
124    exp_class: t.Type[E], args: t.List, bracket_kind: TokenType, dialect: Dialect
125) -> exp.Expression:
126    array_exp = exp_class(expressions=args)
127
128    if exp_class == exp.Array and dialect.HAS_DISTINCT_ARRAY_CONSTRUCTORS:
129        array_exp.set("bracket_notation", bracket_kind == TokenType.L_BRACKET)
130
131    return array_exp
def build_convert_timezone( args: List, default_source_tz: Optional[str] = None) -> Union[sqlglot.expressions.ConvertTimezone, sqlglot.expressions.Anonymous]:
134def build_convert_timezone(
135    args: t.List, default_source_tz: t.Optional[str] = None
136) -> t.Union[exp.ConvertTimezone, exp.Anonymous]:
137    if len(args) == 2:
138        source_tz = exp.Literal.string(default_source_tz) if default_source_tz else None
139        return exp.ConvertTimezone(
140            source_tz=source_tz, target_tz=seq_get(args, 0), timestamp=seq_get(args, 1)
141        )
142
143    return exp.ConvertTimezone.from_arg_list(args)
def build_trim(args: List, is_left: bool = True):
146def build_trim(args: t.List, is_left: bool = True):
147    return exp.Trim(
148        this=seq_get(args, 0),
149        expression=seq_get(args, 1),
150        position="LEADING" if is_left else "TRAILING",
151    )
def build_coalesce( args: List, is_nvl: Optional[bool] = None) -> sqlglot.expressions.Coalesce:
154def build_coalesce(args: t.List, is_nvl: t.Optional[bool] = None) -> exp.Coalesce:
155    return exp.Coalesce(this=seq_get(args, 0), expressions=args[1:], is_nvl=is_nvl)
def build_locate_strposition(args: List):
158def build_locate_strposition(args: t.List):
159    return exp.StrPosition(
160        this=seq_get(args, 1),
161        substr=seq_get(args, 0),
162        position=seq_get(args, 2),
163    )
class Parser:
 176class Parser(metaclass=_Parser):
 177    """
 178    Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.
 179
 180    Args:
 181        error_level: The desired error level.
 182            Default: ErrorLevel.IMMEDIATE
 183        error_message_context: The amount of context to capture from a query string when displaying
 184            the error message (in number of characters).
 185            Default: 100
 186        max_errors: Maximum number of error messages to include in a raised ParseError.
 187            This is only relevant if error_level is ErrorLevel.RAISE.
 188            Default: 3
 189    """
 190
 191    FUNCTIONS: t.Dict[str, t.Callable] = {
 192        **{name: func.from_arg_list for name, func in exp.FUNCTION_BY_NAME.items()},
 193        **dict.fromkeys(("COALESCE", "IFNULL", "NVL"), build_coalesce),
 194        "ARRAY": lambda args, dialect: exp.Array(expressions=args),
 195        "ARRAYAGG": lambda args, dialect: exp.ArrayAgg(
 196            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 197        ),
 198        "ARRAY_AGG": lambda args, dialect: exp.ArrayAgg(
 199            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 200        ),
 201        "CHAR": lambda args: exp.Chr(expressions=args),
 202        "CHR": lambda args: exp.Chr(expressions=args),
 203        "COUNT": lambda args: exp.Count(this=seq_get(args, 0), expressions=args[1:], big_int=True),
 204        "CONCAT": lambda args, dialect: exp.Concat(
 205            expressions=args,
 206            safe=not dialect.STRICT_STRING_CONCAT,
 207            coalesce=dialect.CONCAT_COALESCE,
 208        ),
 209        "CONCAT_WS": lambda args, dialect: exp.ConcatWs(
 210            expressions=args,
 211            safe=not dialect.STRICT_STRING_CONCAT,
 212            coalesce=dialect.CONCAT_COALESCE,
 213        ),
 214        "CONVERT_TIMEZONE": build_convert_timezone,
 215        "DATE_TO_DATE_STR": lambda args: exp.Cast(
 216            this=seq_get(args, 0),
 217            to=exp.DataType(this=exp.DataType.Type.TEXT),
 218        ),
 219        "GENERATE_DATE_ARRAY": lambda args: exp.GenerateDateArray(
 220            start=seq_get(args, 0),
 221            end=seq_get(args, 1),
 222            step=seq_get(args, 2) or exp.Interval(this=exp.Literal.string(1), unit=exp.var("DAY")),
 223        ),
 224        "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)),
 225        "HEX": build_hex,
 226        "JSON_EXTRACT": build_extract_json_with_path(exp.JSONExtract),
 227        "JSON_EXTRACT_SCALAR": build_extract_json_with_path(exp.JSONExtractScalar),
 228        "JSON_EXTRACT_PATH_TEXT": build_extract_json_with_path(exp.JSONExtractScalar),
 229        "LIKE": build_like,
 230        "LOG": build_logarithm,
 231        "LOG2": lambda args: exp.Log(this=exp.Literal.number(2), expression=seq_get(args, 0)),
 232        "LOG10": lambda args: exp.Log(this=exp.Literal.number(10), expression=seq_get(args, 0)),
 233        "LOWER": build_lower,
 234        "LPAD": lambda args: build_pad(args),
 235        "LEFTPAD": lambda args: build_pad(args),
 236        "LTRIM": lambda args: build_trim(args),
 237        "MOD": build_mod,
 238        "RIGHTPAD": lambda args: build_pad(args, is_left=False),
 239        "RPAD": lambda args: build_pad(args, is_left=False),
 240        "RTRIM": lambda args: build_trim(args, is_left=False),
 241        "SCOPE_RESOLUTION": lambda args: exp.ScopeResolution(expression=seq_get(args, 0))
 242        if len(args) != 2
 243        else exp.ScopeResolution(this=seq_get(args, 0), expression=seq_get(args, 1)),
 244        "STRPOS": exp.StrPosition.from_arg_list,
 245        "CHARINDEX": lambda args: build_locate_strposition(args),
 246        "INSTR": exp.StrPosition.from_arg_list,
 247        "LOCATE": lambda args: build_locate_strposition(args),
 248        "TIME_TO_TIME_STR": lambda args: exp.Cast(
 249            this=seq_get(args, 0),
 250            to=exp.DataType(this=exp.DataType.Type.TEXT),
 251        ),
 252        "TO_HEX": build_hex,
 253        "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring(
 254            this=exp.Cast(
 255                this=seq_get(args, 0),
 256                to=exp.DataType(this=exp.DataType.Type.TEXT),
 257            ),
 258            start=exp.Literal.number(1),
 259            length=exp.Literal.number(10),
 260        ),
 261        "UNNEST": lambda args: exp.Unnest(expressions=ensure_list(seq_get(args, 0))),
 262        "UPPER": build_upper,
 263        "VAR_MAP": build_var_map,
 264    }
 265
 266    NO_PAREN_FUNCTIONS = {
 267        TokenType.CURRENT_DATE: exp.CurrentDate,
 268        TokenType.CURRENT_DATETIME: exp.CurrentDate,
 269        TokenType.CURRENT_TIME: exp.CurrentTime,
 270        TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp,
 271        TokenType.CURRENT_USER: exp.CurrentUser,
 272    }
 273
 274    STRUCT_TYPE_TOKENS = {
 275        TokenType.NESTED,
 276        TokenType.OBJECT,
 277        TokenType.STRUCT,
 278        TokenType.UNION,
 279    }
 280
 281    NESTED_TYPE_TOKENS = {
 282        TokenType.ARRAY,
 283        TokenType.LIST,
 284        TokenType.LOWCARDINALITY,
 285        TokenType.MAP,
 286        TokenType.NULLABLE,
 287        TokenType.RANGE,
 288        *STRUCT_TYPE_TOKENS,
 289    }
 290
 291    ENUM_TYPE_TOKENS = {
 292        TokenType.DYNAMIC,
 293        TokenType.ENUM,
 294        TokenType.ENUM8,
 295        TokenType.ENUM16,
 296    }
 297
 298    AGGREGATE_TYPE_TOKENS = {
 299        TokenType.AGGREGATEFUNCTION,
 300        TokenType.SIMPLEAGGREGATEFUNCTION,
 301    }
 302
 303    TYPE_TOKENS = {
 304        TokenType.BIT,
 305        TokenType.BOOLEAN,
 306        TokenType.TINYINT,
 307        TokenType.UTINYINT,
 308        TokenType.SMALLINT,
 309        TokenType.USMALLINT,
 310        TokenType.INT,
 311        TokenType.UINT,
 312        TokenType.BIGINT,
 313        TokenType.UBIGINT,
 314        TokenType.INT128,
 315        TokenType.UINT128,
 316        TokenType.INT256,
 317        TokenType.UINT256,
 318        TokenType.MEDIUMINT,
 319        TokenType.UMEDIUMINT,
 320        TokenType.FIXEDSTRING,
 321        TokenType.FLOAT,
 322        TokenType.DOUBLE,
 323        TokenType.UDOUBLE,
 324        TokenType.CHAR,
 325        TokenType.NCHAR,
 326        TokenType.VARCHAR,
 327        TokenType.NVARCHAR,
 328        TokenType.BPCHAR,
 329        TokenType.TEXT,
 330        TokenType.MEDIUMTEXT,
 331        TokenType.LONGTEXT,
 332        TokenType.BLOB,
 333        TokenType.MEDIUMBLOB,
 334        TokenType.LONGBLOB,
 335        TokenType.BINARY,
 336        TokenType.VARBINARY,
 337        TokenType.JSON,
 338        TokenType.JSONB,
 339        TokenType.INTERVAL,
 340        TokenType.TINYBLOB,
 341        TokenType.TINYTEXT,
 342        TokenType.TIME,
 343        TokenType.TIMETZ,
 344        TokenType.TIMESTAMP,
 345        TokenType.TIMESTAMP_S,
 346        TokenType.TIMESTAMP_MS,
 347        TokenType.TIMESTAMP_NS,
 348        TokenType.TIMESTAMPTZ,
 349        TokenType.TIMESTAMPLTZ,
 350        TokenType.TIMESTAMPNTZ,
 351        TokenType.DATETIME,
 352        TokenType.DATETIME2,
 353        TokenType.DATETIME64,
 354        TokenType.SMALLDATETIME,
 355        TokenType.DATE,
 356        TokenType.DATE32,
 357        TokenType.INT4RANGE,
 358        TokenType.INT4MULTIRANGE,
 359        TokenType.INT8RANGE,
 360        TokenType.INT8MULTIRANGE,
 361        TokenType.NUMRANGE,
 362        TokenType.NUMMULTIRANGE,
 363        TokenType.TSRANGE,
 364        TokenType.TSMULTIRANGE,
 365        TokenType.TSTZRANGE,
 366        TokenType.TSTZMULTIRANGE,
 367        TokenType.DATERANGE,
 368        TokenType.DATEMULTIRANGE,
 369        TokenType.DECIMAL,
 370        TokenType.DECIMAL32,
 371        TokenType.DECIMAL64,
 372        TokenType.DECIMAL128,
 373        TokenType.DECIMAL256,
 374        TokenType.UDECIMAL,
 375        TokenType.BIGDECIMAL,
 376        TokenType.UUID,
 377        TokenType.GEOGRAPHY,
 378        TokenType.GEOMETRY,
 379        TokenType.POINT,
 380        TokenType.RING,
 381        TokenType.LINESTRING,
 382        TokenType.MULTILINESTRING,
 383        TokenType.POLYGON,
 384        TokenType.MULTIPOLYGON,
 385        TokenType.HLLSKETCH,
 386        TokenType.HSTORE,
 387        TokenType.PSEUDO_TYPE,
 388        TokenType.SUPER,
 389        TokenType.SERIAL,
 390        TokenType.SMALLSERIAL,
 391        TokenType.BIGSERIAL,
 392        TokenType.XML,
 393        TokenType.YEAR,
 394        TokenType.USERDEFINED,
 395        TokenType.MONEY,
 396        TokenType.SMALLMONEY,
 397        TokenType.ROWVERSION,
 398        TokenType.IMAGE,
 399        TokenType.VARIANT,
 400        TokenType.VECTOR,
 401        TokenType.VOID,
 402        TokenType.OBJECT,
 403        TokenType.OBJECT_IDENTIFIER,
 404        TokenType.INET,
 405        TokenType.IPADDRESS,
 406        TokenType.IPPREFIX,
 407        TokenType.IPV4,
 408        TokenType.IPV6,
 409        TokenType.UNKNOWN,
 410        TokenType.NOTHING,
 411        TokenType.NULL,
 412        TokenType.NAME,
 413        TokenType.TDIGEST,
 414        TokenType.DYNAMIC,
 415        *ENUM_TYPE_TOKENS,
 416        *NESTED_TYPE_TOKENS,
 417        *AGGREGATE_TYPE_TOKENS,
 418    }
 419
 420    SIGNED_TO_UNSIGNED_TYPE_TOKEN = {
 421        TokenType.BIGINT: TokenType.UBIGINT,
 422        TokenType.INT: TokenType.UINT,
 423        TokenType.MEDIUMINT: TokenType.UMEDIUMINT,
 424        TokenType.SMALLINT: TokenType.USMALLINT,
 425        TokenType.TINYINT: TokenType.UTINYINT,
 426        TokenType.DECIMAL: TokenType.UDECIMAL,
 427        TokenType.DOUBLE: TokenType.UDOUBLE,
 428    }
 429
 430    SUBQUERY_PREDICATES = {
 431        TokenType.ANY: exp.Any,
 432        TokenType.ALL: exp.All,
 433        TokenType.EXISTS: exp.Exists,
 434        TokenType.SOME: exp.Any,
 435    }
 436
 437    RESERVED_TOKENS = {
 438        *Tokenizer.SINGLE_TOKENS.values(),
 439        TokenType.SELECT,
 440    } - {TokenType.IDENTIFIER}
 441
 442    DB_CREATABLES = {
 443        TokenType.DATABASE,
 444        TokenType.DICTIONARY,
 445        TokenType.FILE_FORMAT,
 446        TokenType.MODEL,
 447        TokenType.NAMESPACE,
 448        TokenType.SCHEMA,
 449        TokenType.SEQUENCE,
 450        TokenType.SINK,
 451        TokenType.SOURCE,
 452        TokenType.STAGE,
 453        TokenType.STORAGE_INTEGRATION,
 454        TokenType.STREAMLIT,
 455        TokenType.TABLE,
 456        TokenType.TAG,
 457        TokenType.VIEW,
 458        TokenType.WAREHOUSE,
 459    }
 460
 461    CREATABLES = {
 462        TokenType.COLUMN,
 463        TokenType.CONSTRAINT,
 464        TokenType.FOREIGN_KEY,
 465        TokenType.FUNCTION,
 466        TokenType.INDEX,
 467        TokenType.PROCEDURE,
 468        *DB_CREATABLES,
 469    }
 470
 471    ALTERABLES = {
 472        TokenType.INDEX,
 473        TokenType.TABLE,
 474        TokenType.VIEW,
 475    }
 476
 477    # Tokens that can represent identifiers
 478    ID_VAR_TOKENS = {
 479        TokenType.ALL,
 480        TokenType.ATTACH,
 481        TokenType.VAR,
 482        TokenType.ANTI,
 483        TokenType.APPLY,
 484        TokenType.ASC,
 485        TokenType.ASOF,
 486        TokenType.AUTO_INCREMENT,
 487        TokenType.BEGIN,
 488        TokenType.BPCHAR,
 489        TokenType.CACHE,
 490        TokenType.CASE,
 491        TokenType.COLLATE,
 492        TokenType.COMMAND,
 493        TokenType.COMMENT,
 494        TokenType.COMMIT,
 495        TokenType.CONSTRAINT,
 496        TokenType.COPY,
 497        TokenType.CUBE,
 498        TokenType.CURRENT_SCHEMA,
 499        TokenType.DEFAULT,
 500        TokenType.DELETE,
 501        TokenType.DESC,
 502        TokenType.DESCRIBE,
 503        TokenType.DETACH,
 504        TokenType.DICTIONARY,
 505        TokenType.DIV,
 506        TokenType.END,
 507        TokenType.EXECUTE,
 508        TokenType.EXPORT,
 509        TokenType.ESCAPE,
 510        TokenType.FALSE,
 511        TokenType.FIRST,
 512        TokenType.FILTER,
 513        TokenType.FINAL,
 514        TokenType.FORMAT,
 515        TokenType.FULL,
 516        TokenType.GET,
 517        TokenType.IDENTIFIER,
 518        TokenType.IS,
 519        TokenType.ISNULL,
 520        TokenType.INTERVAL,
 521        TokenType.KEEP,
 522        TokenType.KILL,
 523        TokenType.LEFT,
 524        TokenType.LIMIT,
 525        TokenType.LOAD,
 526        TokenType.MERGE,
 527        TokenType.NATURAL,
 528        TokenType.NEXT,
 529        TokenType.OFFSET,
 530        TokenType.OPERATOR,
 531        TokenType.ORDINALITY,
 532        TokenType.OVERLAPS,
 533        TokenType.OVERWRITE,
 534        TokenType.PARTITION,
 535        TokenType.PERCENT,
 536        TokenType.PIVOT,
 537        TokenType.PRAGMA,
 538        TokenType.PUT,
 539        TokenType.RANGE,
 540        TokenType.RECURSIVE,
 541        TokenType.REFERENCES,
 542        TokenType.REFRESH,
 543        TokenType.RENAME,
 544        TokenType.REPLACE,
 545        TokenType.RIGHT,
 546        TokenType.ROLLUP,
 547        TokenType.ROW,
 548        TokenType.ROWS,
 549        TokenType.SEMI,
 550        TokenType.SET,
 551        TokenType.SETTINGS,
 552        TokenType.SHOW,
 553        TokenType.TEMPORARY,
 554        TokenType.TOP,
 555        TokenType.TRUE,
 556        TokenType.TRUNCATE,
 557        TokenType.UNIQUE,
 558        TokenType.UNNEST,
 559        TokenType.UNPIVOT,
 560        TokenType.UPDATE,
 561        TokenType.USE,
 562        TokenType.VOLATILE,
 563        TokenType.WINDOW,
 564        *CREATABLES,
 565        *SUBQUERY_PREDICATES,
 566        *TYPE_TOKENS,
 567        *NO_PAREN_FUNCTIONS,
 568    }
 569    ID_VAR_TOKENS.remove(TokenType.UNION)
 570
 571    TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
 572        TokenType.ANTI,
 573        TokenType.APPLY,
 574        TokenType.ASOF,
 575        TokenType.FULL,
 576        TokenType.LEFT,
 577        TokenType.LOCK,
 578        TokenType.NATURAL,
 579        TokenType.RIGHT,
 580        TokenType.SEMI,
 581        TokenType.WINDOW,
 582    }
 583
 584    ALIAS_TOKENS = ID_VAR_TOKENS
 585
 586    COLON_PLACEHOLDER_TOKENS = ID_VAR_TOKENS
 587
 588    ARRAY_CONSTRUCTORS = {
 589        "ARRAY": exp.Array,
 590        "LIST": exp.List,
 591    }
 592
 593    COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS}
 594
 595    UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET}
 596
 597    TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"}
 598
 599    FUNC_TOKENS = {
 600        TokenType.COLLATE,
 601        TokenType.COMMAND,
 602        TokenType.CURRENT_DATE,
 603        TokenType.CURRENT_DATETIME,
 604        TokenType.CURRENT_SCHEMA,
 605        TokenType.CURRENT_TIMESTAMP,
 606        TokenType.CURRENT_TIME,
 607        TokenType.CURRENT_USER,
 608        TokenType.FILTER,
 609        TokenType.FIRST,
 610        TokenType.FORMAT,
 611        TokenType.GET,
 612        TokenType.GLOB,
 613        TokenType.IDENTIFIER,
 614        TokenType.INDEX,
 615        TokenType.ISNULL,
 616        TokenType.ILIKE,
 617        TokenType.INSERT,
 618        TokenType.LIKE,
 619        TokenType.MERGE,
 620        TokenType.NEXT,
 621        TokenType.OFFSET,
 622        TokenType.PRIMARY_KEY,
 623        TokenType.RANGE,
 624        TokenType.REPLACE,
 625        TokenType.RLIKE,
 626        TokenType.ROW,
 627        TokenType.UNNEST,
 628        TokenType.VAR,
 629        TokenType.LEFT,
 630        TokenType.RIGHT,
 631        TokenType.SEQUENCE,
 632        TokenType.DATE,
 633        TokenType.DATETIME,
 634        TokenType.TABLE,
 635        TokenType.TIMESTAMP,
 636        TokenType.TIMESTAMPTZ,
 637        TokenType.TRUNCATE,
 638        TokenType.WINDOW,
 639        TokenType.XOR,
 640        *TYPE_TOKENS,
 641        *SUBQUERY_PREDICATES,
 642    }
 643
 644    CONJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 645        TokenType.AND: exp.And,
 646    }
 647
 648    ASSIGNMENT: t.Dict[TokenType, t.Type[exp.Expression]] = {
 649        TokenType.COLON_EQ: exp.PropertyEQ,
 650    }
 651
 652    DISJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 653        TokenType.OR: exp.Or,
 654    }
 655
 656    EQUALITY = {
 657        TokenType.EQ: exp.EQ,
 658        TokenType.NEQ: exp.NEQ,
 659        TokenType.NULLSAFE_EQ: exp.NullSafeEQ,
 660    }
 661
 662    COMPARISON = {
 663        TokenType.GT: exp.GT,
 664        TokenType.GTE: exp.GTE,
 665        TokenType.LT: exp.LT,
 666        TokenType.LTE: exp.LTE,
 667    }
 668
 669    BITWISE = {
 670        TokenType.AMP: exp.BitwiseAnd,
 671        TokenType.CARET: exp.BitwiseXor,
 672        TokenType.PIPE: exp.BitwiseOr,
 673    }
 674
 675    TERM = {
 676        TokenType.DASH: exp.Sub,
 677        TokenType.PLUS: exp.Add,
 678        TokenType.MOD: exp.Mod,
 679        TokenType.COLLATE: exp.Collate,
 680    }
 681
 682    FACTOR = {
 683        TokenType.DIV: exp.IntDiv,
 684        TokenType.LR_ARROW: exp.Distance,
 685        TokenType.SLASH: exp.Div,
 686        TokenType.STAR: exp.Mul,
 687    }
 688
 689    EXPONENT: t.Dict[TokenType, t.Type[exp.Expression]] = {}
 690
 691    TIMES = {
 692        TokenType.TIME,
 693        TokenType.TIMETZ,
 694    }
 695
 696    TIMESTAMPS = {
 697        TokenType.TIMESTAMP,
 698        TokenType.TIMESTAMPNTZ,
 699        TokenType.TIMESTAMPTZ,
 700        TokenType.TIMESTAMPLTZ,
 701        *TIMES,
 702    }
 703
 704    SET_OPERATIONS = {
 705        TokenType.UNION,
 706        TokenType.INTERSECT,
 707        TokenType.EXCEPT,
 708    }
 709
 710    JOIN_METHODS = {
 711        TokenType.ASOF,
 712        TokenType.NATURAL,
 713        TokenType.POSITIONAL,
 714    }
 715
 716    JOIN_SIDES = {
 717        TokenType.LEFT,
 718        TokenType.RIGHT,
 719        TokenType.FULL,
 720    }
 721
 722    JOIN_KINDS = {
 723        TokenType.ANTI,
 724        TokenType.CROSS,
 725        TokenType.INNER,
 726        TokenType.OUTER,
 727        TokenType.SEMI,
 728        TokenType.STRAIGHT_JOIN,
 729    }
 730
 731    JOIN_HINTS: t.Set[str] = set()
 732
 733    LAMBDAS = {
 734        TokenType.ARROW: lambda self, expressions: self.expression(
 735            exp.Lambda,
 736            this=self._replace_lambda(
 737                self._parse_assignment(),
 738                expressions,
 739            ),
 740            expressions=expressions,
 741        ),
 742        TokenType.FARROW: lambda self, expressions: self.expression(
 743            exp.Kwarg,
 744            this=exp.var(expressions[0].name),
 745            expression=self._parse_assignment(),
 746        ),
 747    }
 748
 749    COLUMN_OPERATORS = {
 750        TokenType.DOT: None,
 751        TokenType.DOTCOLON: lambda self, this, to: self.expression(
 752            exp.JSONCast,
 753            this=this,
 754            to=to,
 755        ),
 756        TokenType.DCOLON: lambda self, this, to: self.expression(
 757            exp.Cast if self.STRICT_CAST else exp.TryCast,
 758            this=this,
 759            to=to,
 760        ),
 761        TokenType.ARROW: lambda self, this, path: self.expression(
 762            exp.JSONExtract,
 763            this=this,
 764            expression=self.dialect.to_json_path(path),
 765            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 766        ),
 767        TokenType.DARROW: lambda self, this, path: self.expression(
 768            exp.JSONExtractScalar,
 769            this=this,
 770            expression=self.dialect.to_json_path(path),
 771            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 772        ),
 773        TokenType.HASH_ARROW: lambda self, this, path: self.expression(
 774            exp.JSONBExtract,
 775            this=this,
 776            expression=path,
 777        ),
 778        TokenType.DHASH_ARROW: lambda self, this, path: self.expression(
 779            exp.JSONBExtractScalar,
 780            this=this,
 781            expression=path,
 782        ),
 783        TokenType.PLACEHOLDER: lambda self, this, key: self.expression(
 784            exp.JSONBContains,
 785            this=this,
 786            expression=key,
 787        ),
 788    }
 789
 790    EXPRESSION_PARSERS = {
 791        exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
 792        exp.Column: lambda self: self._parse_column(),
 793        exp.Condition: lambda self: self._parse_assignment(),
 794        exp.DataType: lambda self: self._parse_types(allow_identifiers=False, schema=True),
 795        exp.Expression: lambda self: self._parse_expression(),
 796        exp.From: lambda self: self._parse_from(joins=True),
 797        exp.Group: lambda self: self._parse_group(),
 798        exp.Having: lambda self: self._parse_having(),
 799        exp.Hint: lambda self: self._parse_hint_body(),
 800        exp.Identifier: lambda self: self._parse_id_var(),
 801        exp.Join: lambda self: self._parse_join(),
 802        exp.Lambda: lambda self: self._parse_lambda(),
 803        exp.Lateral: lambda self: self._parse_lateral(),
 804        exp.Limit: lambda self: self._parse_limit(),
 805        exp.Offset: lambda self: self._parse_offset(),
 806        exp.Order: lambda self: self._parse_order(),
 807        exp.Ordered: lambda self: self._parse_ordered(),
 808        exp.Properties: lambda self: self._parse_properties(),
 809        exp.PartitionedByProperty: lambda self: self._parse_partitioned_by(),
 810        exp.Qualify: lambda self: self._parse_qualify(),
 811        exp.Returning: lambda self: self._parse_returning(),
 812        exp.Select: lambda self: self._parse_select(),
 813        exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY),
 814        exp.Table: lambda self: self._parse_table_parts(),
 815        exp.TableAlias: lambda self: self._parse_table_alias(),
 816        exp.Tuple: lambda self: self._parse_value(values=False),
 817        exp.Whens: lambda self: self._parse_when_matched(),
 818        exp.Where: lambda self: self._parse_where(),
 819        exp.Window: lambda self: self._parse_named_window(),
 820        exp.With: lambda self: self._parse_with(),
 821        "JOIN_TYPE": lambda self: self._parse_join_parts(),
 822    }
 823
 824    STATEMENT_PARSERS = {
 825        TokenType.ALTER: lambda self: self._parse_alter(),
 826        TokenType.ANALYZE: lambda self: self._parse_analyze(),
 827        TokenType.BEGIN: lambda self: self._parse_transaction(),
 828        TokenType.CACHE: lambda self: self._parse_cache(),
 829        TokenType.COMMENT: lambda self: self._parse_comment(),
 830        TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(),
 831        TokenType.COPY: lambda self: self._parse_copy(),
 832        TokenType.CREATE: lambda self: self._parse_create(),
 833        TokenType.DELETE: lambda self: self._parse_delete(),
 834        TokenType.DESC: lambda self: self._parse_describe(),
 835        TokenType.DESCRIBE: lambda self: self._parse_describe(),
 836        TokenType.DROP: lambda self: self._parse_drop(),
 837        TokenType.GRANT: lambda self: self._parse_grant(),
 838        TokenType.INSERT: lambda self: self._parse_insert(),
 839        TokenType.KILL: lambda self: self._parse_kill(),
 840        TokenType.LOAD: lambda self: self._parse_load(),
 841        TokenType.MERGE: lambda self: self._parse_merge(),
 842        TokenType.PIVOT: lambda self: self._parse_simplified_pivot(),
 843        TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()),
 844        TokenType.REFRESH: lambda self: self._parse_refresh(),
 845        TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(),
 846        TokenType.SET: lambda self: self._parse_set(),
 847        TokenType.TRUNCATE: lambda self: self._parse_truncate_table(),
 848        TokenType.UNCACHE: lambda self: self._parse_uncache(),
 849        TokenType.UNPIVOT: lambda self: self._parse_simplified_pivot(is_unpivot=True),
 850        TokenType.UPDATE: lambda self: self._parse_update(),
 851        TokenType.USE: lambda self: self._parse_use(),
 852        TokenType.SEMICOLON: lambda self: exp.Semicolon(),
 853    }
 854
 855    UNARY_PARSERS = {
 856        TokenType.PLUS: lambda self: self._parse_unary(),  # Unary + is handled as a no-op
 857        TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()),
 858        TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()),
 859        TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()),
 860        TokenType.PIPE_SLASH: lambda self: self.expression(exp.Sqrt, this=self._parse_unary()),
 861        TokenType.DPIPE_SLASH: lambda self: self.expression(exp.Cbrt, this=self._parse_unary()),
 862    }
 863
 864    STRING_PARSERS = {
 865        TokenType.HEREDOC_STRING: lambda self, token: self.expression(
 866            exp.RawString, this=token.text
 867        ),
 868        TokenType.NATIONAL_STRING: lambda self, token: self.expression(
 869            exp.National, this=token.text
 870        ),
 871        TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text),
 872        TokenType.STRING: lambda self, token: self.expression(
 873            exp.Literal, this=token.text, is_string=True
 874        ),
 875        TokenType.UNICODE_STRING: lambda self, token: self.expression(
 876            exp.UnicodeString,
 877            this=token.text,
 878            escape=self._match_text_seq("UESCAPE") and self._parse_string(),
 879        ),
 880    }
 881
 882    NUMERIC_PARSERS = {
 883        TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text),
 884        TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text),
 885        TokenType.HEX_STRING: lambda self, token: self.expression(
 886            exp.HexString,
 887            this=token.text,
 888            is_integer=self.dialect.HEX_STRING_IS_INTEGER_TYPE or None,
 889        ),
 890        TokenType.NUMBER: lambda self, token: self.expression(
 891            exp.Literal, this=token.text, is_string=False
 892        ),
 893    }
 894
 895    PRIMARY_PARSERS = {
 896        **STRING_PARSERS,
 897        **NUMERIC_PARSERS,
 898        TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token),
 899        TokenType.NULL: lambda self, _: self.expression(exp.Null),
 900        TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True),
 901        TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False),
 902        TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(),
 903        TokenType.STAR: lambda self, _: self._parse_star_ops(),
 904    }
 905
 906    PLACEHOLDER_PARSERS = {
 907        TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
 908        TokenType.PARAMETER: lambda self: self._parse_parameter(),
 909        TokenType.COLON: lambda self: (
 910            self.expression(exp.Placeholder, this=self._prev.text)
 911            if self._match_set(self.COLON_PLACEHOLDER_TOKENS)
 912            else None
 913        ),
 914    }
 915
 916    RANGE_PARSERS = {
 917        TokenType.AT_GT: binary_range_parser(exp.ArrayContainsAll),
 918        TokenType.BETWEEN: lambda self, this: self._parse_between(this),
 919        TokenType.GLOB: binary_range_parser(exp.Glob),
 920        TokenType.ILIKE: binary_range_parser(exp.ILike),
 921        TokenType.IN: lambda self, this: self._parse_in(this),
 922        TokenType.IRLIKE: binary_range_parser(exp.RegexpILike),
 923        TokenType.IS: lambda self, this: self._parse_is(this),
 924        TokenType.LIKE: binary_range_parser(exp.Like),
 925        TokenType.LT_AT: binary_range_parser(exp.ArrayContainsAll, reverse_args=True),
 926        TokenType.OVERLAPS: binary_range_parser(exp.Overlaps),
 927        TokenType.RLIKE: binary_range_parser(exp.RegexpLike),
 928        TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo),
 929        TokenType.FOR: lambda self, this: self._parse_comprehension(this),
 930    }
 931
 932    PROPERTY_PARSERS: t.Dict[str, t.Callable] = {
 933        "ALLOWED_VALUES": lambda self: self.expression(
 934            exp.AllowedValuesProperty, expressions=self._parse_csv(self._parse_primary)
 935        ),
 936        "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty),
 937        "AUTO": lambda self: self._parse_auto_property(),
 938        "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty),
 939        "BACKUP": lambda self: self.expression(
 940            exp.BackupProperty, this=self._parse_var(any_token=True)
 941        ),
 942        "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(),
 943        "CHARSET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 944        "CHARACTER SET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 945        "CHECKSUM": lambda self: self._parse_checksum(),
 946        "CLUSTER BY": lambda self: self._parse_cluster(),
 947        "CLUSTERED": lambda self: self._parse_clustered_by(),
 948        "COLLATE": lambda self, **kwargs: self._parse_property_assignment(
 949            exp.CollateProperty, **kwargs
 950        ),
 951        "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty),
 952        "CONTAINS": lambda self: self._parse_contains_property(),
 953        "COPY": lambda self: self._parse_copy_property(),
 954        "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs),
 955        "DATA_DELETION": lambda self: self._parse_data_deletion_property(),
 956        "DEFINER": lambda self: self._parse_definer(),
 957        "DETERMINISTIC": lambda self: self.expression(
 958            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 959        ),
 960        "DISTRIBUTED": lambda self: self._parse_distributed_property(),
 961        "DUPLICATE": lambda self: self._parse_composite_key_property(exp.DuplicateKeyProperty),
 962        "DYNAMIC": lambda self: self.expression(exp.DynamicProperty),
 963        "DISTKEY": lambda self: self._parse_distkey(),
 964        "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty),
 965        "EMPTY": lambda self: self.expression(exp.EmptyProperty),
 966        "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty),
 967        "ENVIRONMENT": lambda self: self.expression(
 968            exp.EnviromentProperty, expressions=self._parse_wrapped_csv(self._parse_assignment)
 969        ),
 970        "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty),
 971        "EXTERNAL": lambda self: self.expression(exp.ExternalProperty),
 972        "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs),
 973        "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
 974        "FREESPACE": lambda self: self._parse_freespace(),
 975        "GLOBAL": lambda self: self.expression(exp.GlobalProperty),
 976        "HEAP": lambda self: self.expression(exp.HeapProperty),
 977        "ICEBERG": lambda self: self.expression(exp.IcebergProperty),
 978        "IMMUTABLE": lambda self: self.expression(
 979            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 980        ),
 981        "INHERITS": lambda self: self.expression(
 982            exp.InheritsProperty, expressions=self._parse_wrapped_csv(self._parse_table)
 983        ),
 984        "INPUT": lambda self: self.expression(exp.InputModelProperty, this=self._parse_schema()),
 985        "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs),
 986        "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty),
 987        "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"),
 988        "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"),
 989        "LIKE": lambda self: self._parse_create_like(),
 990        "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty),
 991        "LOCK": lambda self: self._parse_locking(),
 992        "LOCKING": lambda self: self._parse_locking(),
 993        "LOG": lambda self, **kwargs: self._parse_log(**kwargs),
 994        "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty),
 995        "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs),
 996        "MODIFIES": lambda self: self._parse_modifies_property(),
 997        "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True),
 998        "NO": lambda self: self._parse_no_property(),
 999        "ON": lambda self: self._parse_on_property(),
1000        "ORDER BY": lambda self: self._parse_order(skip_order_token=True),
1001        "OUTPUT": lambda self: self.expression(exp.OutputModelProperty, this=self._parse_schema()),
1002        "PARTITION": lambda self: self._parse_partitioned_of(),
1003        "PARTITION BY": lambda self: self._parse_partitioned_by(),
1004        "PARTITIONED BY": lambda self: self._parse_partitioned_by(),
1005        "PARTITIONED_BY": lambda self: self._parse_partitioned_by(),
1006        "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True),
1007        "RANGE": lambda self: self._parse_dict_range(this="RANGE"),
1008        "READS": lambda self: self._parse_reads_property(),
1009        "REMOTE": lambda self: self._parse_remote_with_connection(),
1010        "RETURNS": lambda self: self._parse_returns(),
1011        "STRICT": lambda self: self.expression(exp.StrictProperty),
1012        "STREAMING": lambda self: self.expression(exp.StreamingTableProperty),
1013        "ROW": lambda self: self._parse_row(),
1014        "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty),
1015        "SAMPLE": lambda self: self.expression(
1016            exp.SampleProperty, this=self._match_text_seq("BY") and self._parse_bitwise()
1017        ),
1018        "SECURE": lambda self: self.expression(exp.SecureProperty),
1019        "SECURITY": lambda self: self._parse_security(),
1020        "SET": lambda self: self.expression(exp.SetProperty, multi=False),
1021        "SETTINGS": lambda self: self._parse_settings_property(),
1022        "SHARING": lambda self: self._parse_property_assignment(exp.SharingProperty),
1023        "SORTKEY": lambda self: self._parse_sortkey(),
1024        "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"),
1025        "STABLE": lambda self: self.expression(
1026            exp.StabilityProperty, this=exp.Literal.string("STABLE")
1027        ),
1028        "STORED": lambda self: self._parse_stored(),
1029        "SYSTEM_VERSIONING": lambda self: self._parse_system_versioning_property(),
1030        "TBLPROPERTIES": lambda self: self._parse_wrapped_properties(),
1031        "TEMP": lambda self: self.expression(exp.TemporaryProperty),
1032        "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty),
1033        "TO": lambda self: self._parse_to_table(),
1034        "TRANSIENT": lambda self: self.expression(exp.TransientProperty),
1035        "TRANSFORM": lambda self: self.expression(
1036            exp.TransformModelProperty, expressions=self._parse_wrapped_csv(self._parse_expression)
1037        ),
1038        "TTL": lambda self: self._parse_ttl(),
1039        "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
1040        "UNLOGGED": lambda self: self.expression(exp.UnloggedProperty),
1041        "VOLATILE": lambda self: self._parse_volatile_property(),
1042        "WITH": lambda self: self._parse_with_property(),
1043    }
1044
1045    CONSTRAINT_PARSERS = {
1046        "AUTOINCREMENT": lambda self: self._parse_auto_increment(),
1047        "AUTO_INCREMENT": lambda self: self._parse_auto_increment(),
1048        "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False),
1049        "CHARACTER SET": lambda self: self.expression(
1050            exp.CharacterSetColumnConstraint, this=self._parse_var_or_string()
1051        ),
1052        "CHECK": lambda self: self.expression(
1053            exp.CheckColumnConstraint,
1054            this=self._parse_wrapped(self._parse_assignment),
1055            enforced=self._match_text_seq("ENFORCED"),
1056        ),
1057        "COLLATE": lambda self: self.expression(
1058            exp.CollateColumnConstraint,
1059            this=self._parse_identifier() or self._parse_column(),
1060        ),
1061        "COMMENT": lambda self: self.expression(
1062            exp.CommentColumnConstraint, this=self._parse_string()
1063        ),
1064        "COMPRESS": lambda self: self._parse_compress(),
1065        "CLUSTERED": lambda self: self.expression(
1066            exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1067        ),
1068        "NONCLUSTERED": lambda self: self.expression(
1069            exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1070        ),
1071        "DEFAULT": lambda self: self.expression(
1072            exp.DefaultColumnConstraint, this=self._parse_bitwise()
1073        ),
1074        "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()),
1075        "EPHEMERAL": lambda self: self.expression(
1076            exp.EphemeralColumnConstraint, this=self._parse_bitwise()
1077        ),
1078        "EXCLUDE": lambda self: self.expression(
1079            exp.ExcludeColumnConstraint, this=self._parse_index_params()
1080        ),
1081        "FOREIGN KEY": lambda self: self._parse_foreign_key(),
1082        "FORMAT": lambda self: self.expression(
1083            exp.DateFormatColumnConstraint, this=self._parse_var_or_string()
1084        ),
1085        "GENERATED": lambda self: self._parse_generated_as_identity(),
1086        "IDENTITY": lambda self: self._parse_auto_increment(),
1087        "INLINE": lambda self: self._parse_inline(),
1088        "LIKE": lambda self: self._parse_create_like(),
1089        "NOT": lambda self: self._parse_not_constraint(),
1090        "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True),
1091        "ON": lambda self: (
1092            self._match(TokenType.UPDATE)
1093            and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function())
1094        )
1095        or self.expression(exp.OnProperty, this=self._parse_id_var()),
1096        "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()),
1097        "PERIOD": lambda self: self._parse_period_for_system_time(),
1098        "PRIMARY KEY": lambda self: self._parse_primary_key(),
1099        "REFERENCES": lambda self: self._parse_references(match=False),
1100        "TITLE": lambda self: self.expression(
1101            exp.TitleColumnConstraint, this=self._parse_var_or_string()
1102        ),
1103        "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]),
1104        "UNIQUE": lambda self: self._parse_unique(),
1105        "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint),
1106        "WATERMARK": lambda self: self.expression(
1107            exp.WatermarkColumnConstraint,
1108            this=self._match(TokenType.FOR) and self._parse_column(),
1109            expression=self._match(TokenType.ALIAS) and self._parse_disjunction(),
1110        ),
1111        "WITH": lambda self: self.expression(
1112            exp.Properties, expressions=self._parse_wrapped_properties()
1113        ),
1114        "BUCKET": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1115        "TRUNCATE": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1116    }
1117
1118    def _parse_partitioned_by_bucket_or_truncate(self) -> exp.Expression:
1119        klass = (
1120            exp.PartitionedByBucket
1121            if self._prev.text.upper() == "BUCKET"
1122            else exp.PartitionByTruncate
1123        )
1124
1125        args = self._parse_wrapped_csv(lambda: self._parse_primary() or self._parse_column())
1126        this, expression = seq_get(args, 0), seq_get(args, 1)
1127
1128        if isinstance(this, exp.Literal):
1129            # Check for Iceberg partition transforms (bucket / truncate) and ensure their arguments are in the right order
1130            #  - For Hive, it's `bucket(<num buckets>, <col name>)` or `truncate(<num_chars>, <col_name>)`
1131            #  - For Trino, it's reversed - `bucket(<col name>, <num buckets>)` or `truncate(<col_name>, <num_chars>)`
1132            # Both variants are canonicalized in the latter i.e `bucket(<col name>, <num buckets>)`
1133            #
1134            # Hive ref: https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-partitioning
1135            # Trino ref: https://docs.aws.amazon.com/athena/latest/ug/create-table-as.html#ctas-table-properties
1136            this, expression = expression, this
1137
1138        return self.expression(klass, this=this, expression=expression)
1139
1140    ALTER_PARSERS = {
1141        "ADD": lambda self: self._parse_alter_table_add(),
1142        "AS": lambda self: self._parse_select(),
1143        "ALTER": lambda self: self._parse_alter_table_alter(),
1144        "CLUSTER BY": lambda self: self._parse_cluster(wrapped=True),
1145        "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()),
1146        "DROP": lambda self: self._parse_alter_table_drop(),
1147        "RENAME": lambda self: self._parse_alter_table_rename(),
1148        "SET": lambda self: self._parse_alter_table_set(),
1149        "SWAP": lambda self: self.expression(
1150            exp.SwapTable, this=self._match(TokenType.WITH) and self._parse_table(schema=True)
1151        ),
1152    }
1153
1154    ALTER_ALTER_PARSERS = {
1155        "DISTKEY": lambda self: self._parse_alter_diststyle(),
1156        "DISTSTYLE": lambda self: self._parse_alter_diststyle(),
1157        "SORTKEY": lambda self: self._parse_alter_sortkey(),
1158        "COMPOUND": lambda self: self._parse_alter_sortkey(compound=True),
1159    }
1160
1161    SCHEMA_UNNAMED_CONSTRAINTS = {
1162        "CHECK",
1163        "EXCLUDE",
1164        "FOREIGN KEY",
1165        "LIKE",
1166        "PERIOD",
1167        "PRIMARY KEY",
1168        "UNIQUE",
1169        "WATERMARK",
1170        "BUCKET",
1171        "TRUNCATE",
1172    }
1173
1174    NO_PAREN_FUNCTION_PARSERS = {
1175        "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()),
1176        "CASE": lambda self: self._parse_case(),
1177        "CONNECT_BY_ROOT": lambda self: self.expression(
1178            exp.ConnectByRoot, this=self._parse_column()
1179        ),
1180        "IF": lambda self: self._parse_if(),
1181    }
1182
1183    INVALID_FUNC_NAME_TOKENS = {
1184        TokenType.IDENTIFIER,
1185        TokenType.STRING,
1186    }
1187
1188    FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
1189
1190    KEY_VALUE_DEFINITIONS = (exp.Alias, exp.EQ, exp.PropertyEQ, exp.Slice)
1191
1192    FUNCTION_PARSERS = {
1193        **{
1194            name: lambda self: self._parse_max_min_by(exp.ArgMax) for name in exp.ArgMax.sql_names()
1195        },
1196        **{
1197            name: lambda self: self._parse_max_min_by(exp.ArgMin) for name in exp.ArgMin.sql_names()
1198        },
1199        "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
1200        "CEIL": lambda self: self._parse_ceil_floor(exp.Ceil),
1201        "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
1202        "DECODE": lambda self: self._parse_decode(),
1203        "EXTRACT": lambda self: self._parse_extract(),
1204        "FLOOR": lambda self: self._parse_ceil_floor(exp.Floor),
1205        "GAP_FILL": lambda self: self._parse_gap_fill(),
1206        "JSON_OBJECT": lambda self: self._parse_json_object(),
1207        "JSON_OBJECTAGG": lambda self: self._parse_json_object(agg=True),
1208        "JSON_TABLE": lambda self: self._parse_json_table(),
1209        "MATCH": lambda self: self._parse_match_against(),
1210        "NORMALIZE": lambda self: self._parse_normalize(),
1211        "OPENJSON": lambda self: self._parse_open_json(),
1212        "OVERLAY": lambda self: self._parse_overlay(),
1213        "POSITION": lambda self: self._parse_position(),
1214        "PREDICT": lambda self: self._parse_predict(),
1215        "SAFE_CAST": lambda self: self._parse_cast(False, safe=True),
1216        "STRING_AGG": lambda self: self._parse_string_agg(),
1217        "SUBSTRING": lambda self: self._parse_substring(),
1218        "TRIM": lambda self: self._parse_trim(),
1219        "TRY_CAST": lambda self: self._parse_cast(False, safe=True),
1220        "TRY_CONVERT": lambda self: self._parse_convert(False, safe=True),
1221        "XMLELEMENT": lambda self: self.expression(
1222            exp.XMLElement,
1223            this=self._match_text_seq("NAME") and self._parse_id_var(),
1224            expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
1225        ),
1226        "XMLTABLE": lambda self: self._parse_xml_table(),
1227    }
1228
1229    QUERY_MODIFIER_PARSERS = {
1230        TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()),
1231        TokenType.PREWHERE: lambda self: ("prewhere", self._parse_prewhere()),
1232        TokenType.WHERE: lambda self: ("where", self._parse_where()),
1233        TokenType.GROUP_BY: lambda self: ("group", self._parse_group()),
1234        TokenType.HAVING: lambda self: ("having", self._parse_having()),
1235        TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()),
1236        TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()),
1237        TokenType.ORDER_BY: lambda self: ("order", self._parse_order()),
1238        TokenType.LIMIT: lambda self: ("limit", self._parse_limit()),
1239        TokenType.FETCH: lambda self: ("limit", self._parse_limit()),
1240        TokenType.OFFSET: lambda self: ("offset", self._parse_offset()),
1241        TokenType.FOR: lambda self: ("locks", self._parse_locks()),
1242        TokenType.LOCK: lambda self: ("locks", self._parse_locks()),
1243        TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1244        TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1245        TokenType.CLUSTER_BY: lambda self: (
1246            "cluster",
1247            self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
1248        ),
1249        TokenType.DISTRIBUTE_BY: lambda self: (
1250            "distribute",
1251            self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY),
1252        ),
1253        TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)),
1254        TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)),
1255        TokenType.START_WITH: lambda self: ("connect", self._parse_connect()),
1256    }
1257
1258    SET_PARSERS = {
1259        "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"),
1260        "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"),
1261        "SESSION": lambda self: self._parse_set_item_assignment("SESSION"),
1262        "TRANSACTION": lambda self: self._parse_set_transaction(),
1263    }
1264
1265    SHOW_PARSERS: t.Dict[str, t.Callable] = {}
1266
1267    TYPE_LITERAL_PARSERS = {
1268        exp.DataType.Type.JSON: lambda self, this, _: self.expression(exp.ParseJSON, this=this),
1269    }
1270
1271    TYPE_CONVERTERS: t.Dict[exp.DataType.Type, t.Callable[[exp.DataType], exp.DataType]] = {}
1272
1273    DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN}
1274
1275    PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE}
1276
1277    TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"}
1278    TRANSACTION_CHARACTERISTICS: OPTIONS_TYPE = {
1279        "ISOLATION": (
1280            ("LEVEL", "REPEATABLE", "READ"),
1281            ("LEVEL", "READ", "COMMITTED"),
1282            ("LEVEL", "READ", "UNCOMITTED"),
1283            ("LEVEL", "SERIALIZABLE"),
1284        ),
1285        "READ": ("WRITE", "ONLY"),
1286    }
1287
1288    CONFLICT_ACTIONS: OPTIONS_TYPE = dict.fromkeys(
1289        ("ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK", "UPDATE"), tuple()
1290    )
1291    CONFLICT_ACTIONS["DO"] = ("NOTHING", "UPDATE")
1292
1293    CREATE_SEQUENCE: OPTIONS_TYPE = {
1294        "SCALE": ("EXTEND", "NOEXTEND"),
1295        "SHARD": ("EXTEND", "NOEXTEND"),
1296        "NO": ("CYCLE", "CACHE", "MAXVALUE", "MINVALUE"),
1297        **dict.fromkeys(
1298            (
1299                "SESSION",
1300                "GLOBAL",
1301                "KEEP",
1302                "NOKEEP",
1303                "ORDER",
1304                "NOORDER",
1305                "NOCACHE",
1306                "CYCLE",
1307                "NOCYCLE",
1308                "NOMINVALUE",
1309                "NOMAXVALUE",
1310                "NOSCALE",
1311                "NOSHARD",
1312            ),
1313            tuple(),
1314        ),
1315    }
1316
1317    ISOLATED_LOADING_OPTIONS: OPTIONS_TYPE = {"FOR": ("ALL", "INSERT", "NONE")}
1318
1319    USABLES: OPTIONS_TYPE = dict.fromkeys(
1320        ("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA", "CATALOG"), tuple()
1321    )
1322
1323    CAST_ACTIONS: OPTIONS_TYPE = dict.fromkeys(("RENAME", "ADD"), ("FIELDS",))
1324
1325    SCHEMA_BINDING_OPTIONS: OPTIONS_TYPE = {
1326        "TYPE": ("EVOLUTION",),
1327        **dict.fromkeys(("BINDING", "COMPENSATION", "EVOLUTION"), tuple()),
1328    }
1329
1330    PROCEDURE_OPTIONS: OPTIONS_TYPE = {}
1331
1332    EXECUTE_AS_OPTIONS: OPTIONS_TYPE = dict.fromkeys(("CALLER", "SELF", "OWNER"), tuple())
1333
1334    KEY_CONSTRAINT_OPTIONS: OPTIONS_TYPE = {
1335        "NOT": ("ENFORCED",),
1336        "MATCH": (
1337            "FULL",
1338            "PARTIAL",
1339            "SIMPLE",
1340        ),
1341        "INITIALLY": ("DEFERRED", "IMMEDIATE"),
1342        "USING": (
1343            "BTREE",
1344            "HASH",
1345        ),
1346        **dict.fromkeys(("DEFERRABLE", "NORELY", "RELY"), tuple()),
1347    }
1348
1349    WINDOW_EXCLUDE_OPTIONS: OPTIONS_TYPE = {
1350        "NO": ("OTHERS",),
1351        "CURRENT": ("ROW",),
1352        **dict.fromkeys(("GROUP", "TIES"), tuple()),
1353    }
1354
1355    INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"}
1356
1357    CLONE_KEYWORDS = {"CLONE", "COPY"}
1358    HISTORICAL_DATA_PREFIX = {"AT", "BEFORE", "END"}
1359    HISTORICAL_DATA_KIND = {"TIMESTAMP", "OFFSET", "STATEMENT", "STREAM"}
1360
1361    OPCLASS_FOLLOW_KEYWORDS = {"ASC", "DESC", "NULLS", "WITH"}
1362
1363    OPTYPE_FOLLOW_TOKENS = {TokenType.COMMA, TokenType.R_PAREN}
1364
1365    TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE}
1366
1367    VIEW_ATTRIBUTES = {"ENCRYPTION", "SCHEMABINDING", "VIEW_METADATA"}
1368
1369    WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS}
1370    WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER}
1371    WINDOW_SIDES = {"FOLLOWING", "PRECEDING"}
1372
1373    JSON_KEY_VALUE_SEPARATOR_TOKENS = {TokenType.COLON, TokenType.COMMA, TokenType.IS}
1374
1375    FETCH_TOKENS = ID_VAR_TOKENS - {TokenType.ROW, TokenType.ROWS, TokenType.PERCENT}
1376
1377    ADD_CONSTRAINT_TOKENS = {
1378        TokenType.CONSTRAINT,
1379        TokenType.FOREIGN_KEY,
1380        TokenType.INDEX,
1381        TokenType.KEY,
1382        TokenType.PRIMARY_KEY,
1383        TokenType.UNIQUE,
1384    }
1385
1386    DISTINCT_TOKENS = {TokenType.DISTINCT}
1387
1388    NULL_TOKENS = {TokenType.NULL}
1389
1390    UNNEST_OFFSET_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - SET_OPERATIONS
1391
1392    SELECT_START_TOKENS = {TokenType.L_PAREN, TokenType.WITH, TokenType.SELECT}
1393
1394    COPY_INTO_VARLEN_OPTIONS = {"FILE_FORMAT", "COPY_OPTIONS", "FORMAT_OPTIONS", "CREDENTIAL"}
1395
1396    IS_JSON_PREDICATE_KIND = {"VALUE", "SCALAR", "ARRAY", "OBJECT"}
1397
1398    ODBC_DATETIME_LITERALS = {
1399        "d": exp.Date,
1400        "t": exp.Time,
1401        "ts": exp.Timestamp,
1402    }
1403
1404    ON_CONDITION_TOKENS = {"ERROR", "NULL", "TRUE", "FALSE", "EMPTY"}
1405
1406    PRIVILEGE_FOLLOW_TOKENS = {TokenType.ON, TokenType.COMMA, TokenType.L_PAREN}
1407
1408    # The style options for the DESCRIBE statement
1409    DESCRIBE_STYLES = {"ANALYZE", "EXTENDED", "FORMATTED", "HISTORY"}
1410
1411    # The style options for the ANALYZE statement
1412    ANALYZE_STYLES = {
1413        "BUFFER_USAGE_LIMIT",
1414        "FULL",
1415        "LOCAL",
1416        "NO_WRITE_TO_BINLOG",
1417        "SAMPLE",
1418        "SKIP_LOCKED",
1419        "VERBOSE",
1420    }
1421
1422    ANALYZE_EXPRESSION_PARSERS = {
1423        "ALL": lambda self: self._parse_analyze_columns(),
1424        "COMPUTE": lambda self: self._parse_analyze_statistics(),
1425        "DELETE": lambda self: self._parse_analyze_delete(),
1426        "DROP": lambda self: self._parse_analyze_histogram(),
1427        "ESTIMATE": lambda self: self._parse_analyze_statistics(),
1428        "LIST": lambda self: self._parse_analyze_list(),
1429        "PREDICATE": lambda self: self._parse_analyze_columns(),
1430        "UPDATE": lambda self: self._parse_analyze_histogram(),
1431        "VALIDATE": lambda self: self._parse_analyze_validate(),
1432    }
1433
1434    PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
1435
1436    AMBIGUOUS_ALIAS_TOKENS = (TokenType.LIMIT, TokenType.OFFSET)
1437
1438    OPERATION_MODIFIERS: t.Set[str] = set()
1439
1440    RECURSIVE_CTE_SEARCH_KIND = {"BREADTH", "DEPTH", "CYCLE"}
1441
1442    MODIFIABLES = (exp.Query, exp.Table, exp.TableFromRows)
1443
1444    STRICT_CAST = True
1445
1446    PREFIXED_PIVOT_COLUMNS = False
1447    IDENTIFY_PIVOT_STRINGS = False
1448
1449    LOG_DEFAULTS_TO_LN = False
1450
1451    # Whether ADD is present for each column added by ALTER TABLE
1452    ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN = True
1453
1454    # Whether the table sample clause expects CSV syntax
1455    TABLESAMPLE_CSV = False
1456
1457    # The default method used for table sampling
1458    DEFAULT_SAMPLING_METHOD: t.Optional[str] = None
1459
1460    # Whether the SET command needs a delimiter (e.g. "=") for assignments
1461    SET_REQUIRES_ASSIGNMENT_DELIMITER = True
1462
1463    # Whether the TRIM function expects the characters to trim as its first argument
1464    TRIM_PATTERN_FIRST = False
1465
1466    # Whether string aliases are supported `SELECT COUNT(*) 'count'`
1467    STRING_ALIASES = False
1468
1469    # Whether query modifiers such as LIMIT are attached to the UNION node (vs its right operand)
1470    MODIFIERS_ATTACHED_TO_SET_OP = True
1471    SET_OP_MODIFIERS = {"order", "limit", "offset"}
1472
1473    # Whether to parse IF statements that aren't followed by a left parenthesis as commands
1474    NO_PAREN_IF_COMMANDS = True
1475
1476    # Whether the -> and ->> operators expect documents of type JSON (e.g. Postgres)
1477    JSON_ARROWS_REQUIRE_JSON_TYPE = False
1478
1479    # Whether the `:` operator is used to extract a value from a VARIANT column
1480    COLON_IS_VARIANT_EXTRACT = False
1481
1482    # Whether or not a VALUES keyword needs to be followed by '(' to form a VALUES clause.
1483    # If this is True and '(' is not found, the keyword will be treated as an identifier
1484    VALUES_FOLLOWED_BY_PAREN = True
1485
1486    # Whether implicit unnesting is supported, e.g. SELECT 1 FROM y.z AS z, z.a (Redshift)
1487    SUPPORTS_IMPLICIT_UNNEST = False
1488
1489    # Whether or not interval spans are supported, INTERVAL 1 YEAR TO MONTHS
1490    INTERVAL_SPANS = True
1491
1492    # Whether a PARTITION clause can follow a table reference
1493    SUPPORTS_PARTITION_SELECTION = False
1494
1495    # Whether the `name AS expr` schema/column constraint requires parentheses around `expr`
1496    WRAPPED_TRANSFORM_COLUMN_CONSTRAINT = True
1497
1498    # Whether the 'AS' keyword is optional in the CTE definition syntax
1499    OPTIONAL_ALIAS_TOKEN_CTE = True
1500
1501    __slots__ = (
1502        "error_level",
1503        "error_message_context",
1504        "max_errors",
1505        "dialect",
1506        "sql",
1507        "errors",
1508        "_tokens",
1509        "_index",
1510        "_curr",
1511        "_next",
1512        "_prev",
1513        "_prev_comments",
1514    )
1515
1516    # Autofilled
1517    SHOW_TRIE: t.Dict = {}
1518    SET_TRIE: t.Dict = {}
1519
1520    def __init__(
1521        self,
1522        error_level: t.Optional[ErrorLevel] = None,
1523        error_message_context: int = 100,
1524        max_errors: int = 3,
1525        dialect: DialectType = None,
1526    ):
1527        from sqlglot.dialects import Dialect
1528
1529        self.error_level = error_level or ErrorLevel.IMMEDIATE
1530        self.error_message_context = error_message_context
1531        self.max_errors = max_errors
1532        self.dialect = Dialect.get_or_raise(dialect)
1533        self.reset()
1534
1535    def reset(self):
1536        self.sql = ""
1537        self.errors = []
1538        self._tokens = []
1539        self._index = 0
1540        self._curr = None
1541        self._next = None
1542        self._prev = None
1543        self._prev_comments = None
1544
1545    def parse(
1546        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1547    ) -> t.List[t.Optional[exp.Expression]]:
1548        """
1549        Parses a list of tokens and returns a list of syntax trees, one tree
1550        per parsed SQL statement.
1551
1552        Args:
1553            raw_tokens: The list of tokens.
1554            sql: The original SQL string, used to produce helpful debug messages.
1555
1556        Returns:
1557            The list of the produced syntax trees.
1558        """
1559        return self._parse(
1560            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1561        )
1562
1563    def parse_into(
1564        self,
1565        expression_types: exp.IntoType,
1566        raw_tokens: t.List[Token],
1567        sql: t.Optional[str] = None,
1568    ) -> t.List[t.Optional[exp.Expression]]:
1569        """
1570        Parses a list of tokens into a given Expression type. If a collection of Expression
1571        types is given instead, this method will try to parse the token list into each one
1572        of them, stopping at the first for which the parsing succeeds.
1573
1574        Args:
1575            expression_types: The expression type(s) to try and parse the token list into.
1576            raw_tokens: The list of tokens.
1577            sql: The original SQL string, used to produce helpful debug messages.
1578
1579        Returns:
1580            The target Expression.
1581        """
1582        errors = []
1583        for expression_type in ensure_list(expression_types):
1584            parser = self.EXPRESSION_PARSERS.get(expression_type)
1585            if not parser:
1586                raise TypeError(f"No parser registered for {expression_type}")
1587
1588            try:
1589                return self._parse(parser, raw_tokens, sql)
1590            except ParseError as e:
1591                e.errors[0]["into_expression"] = expression_type
1592                errors.append(e)
1593
1594        raise ParseError(
1595            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1596            errors=merge_errors(errors),
1597        ) from errors[-1]
1598
1599    def _parse(
1600        self,
1601        parse_method: t.Callable[[Parser], t.Optional[exp.Expression]],
1602        raw_tokens: t.List[Token],
1603        sql: t.Optional[str] = None,
1604    ) -> t.List[t.Optional[exp.Expression]]:
1605        self.reset()
1606        self.sql = sql or ""
1607
1608        total = len(raw_tokens)
1609        chunks: t.List[t.List[Token]] = [[]]
1610
1611        for i, token in enumerate(raw_tokens):
1612            if token.token_type == TokenType.SEMICOLON:
1613                if token.comments:
1614                    chunks.append([token])
1615
1616                if i < total - 1:
1617                    chunks.append([])
1618            else:
1619                chunks[-1].append(token)
1620
1621        expressions = []
1622
1623        for tokens in chunks:
1624            self._index = -1
1625            self._tokens = tokens
1626            self._advance()
1627
1628            expressions.append(parse_method(self))
1629
1630            if self._index < len(self._tokens):
1631                self.raise_error("Invalid expression / Unexpected token")
1632
1633            self.check_errors()
1634
1635        return expressions
1636
1637    def check_errors(self) -> None:
1638        """Logs or raises any found errors, depending on the chosen error level setting."""
1639        if self.error_level == ErrorLevel.WARN:
1640            for error in self.errors:
1641                logger.error(str(error))
1642        elif self.error_level == ErrorLevel.RAISE and self.errors:
1643            raise ParseError(
1644                concat_messages(self.errors, self.max_errors),
1645                errors=merge_errors(self.errors),
1646            )
1647
1648    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1649        """
1650        Appends an error in the list of recorded errors or raises it, depending on the chosen
1651        error level setting.
1652        """
1653        token = token or self._curr or self._prev or Token.string("")
1654        start = token.start
1655        end = token.end + 1
1656        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1657        highlight = self.sql[start:end]
1658        end_context = self.sql[end : end + self.error_message_context]
1659
1660        error = ParseError.new(
1661            f"{message}. Line {token.line}, Col: {token.col}.\n"
1662            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1663            description=message,
1664            line=token.line,
1665            col=token.col,
1666            start_context=start_context,
1667            highlight=highlight,
1668            end_context=end_context,
1669        )
1670
1671        if self.error_level == ErrorLevel.IMMEDIATE:
1672            raise error
1673
1674        self.errors.append(error)
1675
1676    def expression(
1677        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1678    ) -> E:
1679        """
1680        Creates a new, validated Expression.
1681
1682        Args:
1683            exp_class: The expression class to instantiate.
1684            comments: An optional list of comments to attach to the expression.
1685            kwargs: The arguments to set for the expression along with their respective values.
1686
1687        Returns:
1688            The target expression.
1689        """
1690        instance = exp_class(**kwargs)
1691        instance.add_comments(comments) if comments else self._add_comments(instance)
1692        return self.validate_expression(instance)
1693
1694    def _add_comments(self, expression: t.Optional[exp.Expression]) -> None:
1695        if expression and self._prev_comments:
1696            expression.add_comments(self._prev_comments)
1697            self._prev_comments = None
1698
1699    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1700        """
1701        Validates an Expression, making sure that all its mandatory arguments are set.
1702
1703        Args:
1704            expression: The expression to validate.
1705            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1706
1707        Returns:
1708            The validated expression.
1709        """
1710        if self.error_level != ErrorLevel.IGNORE:
1711            for error_message in expression.error_messages(args):
1712                self.raise_error(error_message)
1713
1714        return expression
1715
1716    def _find_sql(self, start: Token, end: Token) -> str:
1717        return self.sql[start.start : end.end + 1]
1718
1719    def _is_connected(self) -> bool:
1720        return self._prev and self._curr and self._prev.end + 1 == self._curr.start
1721
1722    def _advance(self, times: int = 1) -> None:
1723        self._index += times
1724        self._curr = seq_get(self._tokens, self._index)
1725        self._next = seq_get(self._tokens, self._index + 1)
1726
1727        if self._index > 0:
1728            self._prev = self._tokens[self._index - 1]
1729            self._prev_comments = self._prev.comments
1730        else:
1731            self._prev = None
1732            self._prev_comments = None
1733
1734    def _retreat(self, index: int) -> None:
1735        if index != self._index:
1736            self._advance(index - self._index)
1737
1738    def _warn_unsupported(self) -> None:
1739        if len(self._tokens) <= 1:
1740            return
1741
1742        # We use _find_sql because self.sql may comprise multiple chunks, and we're only
1743        # interested in emitting a warning for the one being currently processed.
1744        sql = self._find_sql(self._tokens[0], self._tokens[-1])[: self.error_message_context]
1745
1746        logger.warning(
1747            f"'{sql}' contains unsupported syntax. Falling back to parsing as a 'Command'."
1748        )
1749
1750    def _parse_command(self) -> exp.Command:
1751        self._warn_unsupported()
1752        return self.expression(
1753            exp.Command,
1754            comments=self._prev_comments,
1755            this=self._prev.text.upper(),
1756            expression=self._parse_string(),
1757        )
1758
1759    def _try_parse(self, parse_method: t.Callable[[], T], retreat: bool = False) -> t.Optional[T]:
1760        """
1761        Attemps to backtrack if a parse function that contains a try/catch internally raises an error.
1762        This behavior can be different depending on the uset-set ErrorLevel, so _try_parse aims to
1763        solve this by setting & resetting the parser state accordingly
1764        """
1765        index = self._index
1766        error_level = self.error_level
1767
1768        self.error_level = ErrorLevel.IMMEDIATE
1769        try:
1770            this = parse_method()
1771        except ParseError:
1772            this = None
1773        finally:
1774            if not this or retreat:
1775                self._retreat(index)
1776            self.error_level = error_level
1777
1778        return this
1779
1780    def _parse_comment(self, allow_exists: bool = True) -> exp.Expression:
1781        start = self._prev
1782        exists = self._parse_exists() if allow_exists else None
1783
1784        self._match(TokenType.ON)
1785
1786        materialized = self._match_text_seq("MATERIALIZED")
1787        kind = self._match_set(self.CREATABLES) and self._prev
1788        if not kind:
1789            return self._parse_as_command(start)
1790
1791        if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1792            this = self._parse_user_defined_function(kind=kind.token_type)
1793        elif kind.token_type == TokenType.TABLE:
1794            this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS)
1795        elif kind.token_type == TokenType.COLUMN:
1796            this = self._parse_column()
1797        else:
1798            this = self._parse_id_var()
1799
1800        self._match(TokenType.IS)
1801
1802        return self.expression(
1803            exp.Comment,
1804            this=this,
1805            kind=kind.text,
1806            expression=self._parse_string(),
1807            exists=exists,
1808            materialized=materialized,
1809        )
1810
1811    def _parse_to_table(
1812        self,
1813    ) -> exp.ToTableProperty:
1814        table = self._parse_table_parts(schema=True)
1815        return self.expression(exp.ToTableProperty, this=table)
1816
1817    # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl
1818    def _parse_ttl(self) -> exp.Expression:
1819        def _parse_ttl_action() -> t.Optional[exp.Expression]:
1820            this = self._parse_bitwise()
1821
1822            if self._match_text_seq("DELETE"):
1823                return self.expression(exp.MergeTreeTTLAction, this=this, delete=True)
1824            if self._match_text_seq("RECOMPRESS"):
1825                return self.expression(
1826                    exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise()
1827                )
1828            if self._match_text_seq("TO", "DISK"):
1829                return self.expression(
1830                    exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string()
1831                )
1832            if self._match_text_seq("TO", "VOLUME"):
1833                return self.expression(
1834                    exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string()
1835                )
1836
1837            return this
1838
1839        expressions = self._parse_csv(_parse_ttl_action)
1840        where = self._parse_where()
1841        group = self._parse_group()
1842
1843        aggregates = None
1844        if group and self._match(TokenType.SET):
1845            aggregates = self._parse_csv(self._parse_set_item)
1846
1847        return self.expression(
1848            exp.MergeTreeTTL,
1849            expressions=expressions,
1850            where=where,
1851            group=group,
1852            aggregates=aggregates,
1853        )
1854
1855    def _parse_statement(self) -> t.Optional[exp.Expression]:
1856        if self._curr is None:
1857            return None
1858
1859        if self._match_set(self.STATEMENT_PARSERS):
1860            comments = self._prev_comments
1861            stmt = self.STATEMENT_PARSERS[self._prev.token_type](self)
1862            stmt.add_comments(comments, prepend=True)
1863            return stmt
1864
1865        if self._match_set(self.dialect.tokenizer.COMMANDS):
1866            return self._parse_command()
1867
1868        expression = self._parse_expression()
1869        expression = self._parse_set_operations(expression) if expression else self._parse_select()
1870        return self._parse_query_modifiers(expression)
1871
1872    def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command:
1873        start = self._prev
1874        temporary = self._match(TokenType.TEMPORARY)
1875        materialized = self._match_text_seq("MATERIALIZED")
1876
1877        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
1878        if not kind:
1879            return self._parse_as_command(start)
1880
1881        concurrently = self._match_text_seq("CONCURRENTLY")
1882        if_exists = exists or self._parse_exists()
1883
1884        if kind == "COLUMN":
1885            this = self._parse_column()
1886        else:
1887            this = self._parse_table_parts(
1888                schema=True, is_db_reference=self._prev.token_type == TokenType.SCHEMA
1889            )
1890
1891        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
1892
1893        if self._match(TokenType.L_PAREN, advance=False):
1894            expressions = self._parse_wrapped_csv(self._parse_types)
1895        else:
1896            expressions = None
1897
1898        return self.expression(
1899            exp.Drop,
1900            exists=if_exists,
1901            this=this,
1902            expressions=expressions,
1903            kind=self.dialect.CREATABLE_KIND_MAPPING.get(kind) or kind,
1904            temporary=temporary,
1905            materialized=materialized,
1906            cascade=self._match_text_seq("CASCADE"),
1907            constraints=self._match_text_seq("CONSTRAINTS"),
1908            purge=self._match_text_seq("PURGE"),
1909            cluster=cluster,
1910            concurrently=concurrently,
1911        )
1912
1913    def _parse_exists(self, not_: bool = False) -> t.Optional[bool]:
1914        return (
1915            self._match_text_seq("IF")
1916            and (not not_ or self._match(TokenType.NOT))
1917            and self._match(TokenType.EXISTS)
1918        )
1919
1920    def _parse_create(self) -> exp.Create | exp.Command:
1921        # Note: this can't be None because we've matched a statement parser
1922        start = self._prev
1923
1924        replace = (
1925            start.token_type == TokenType.REPLACE
1926            or self._match_pair(TokenType.OR, TokenType.REPLACE)
1927            or self._match_pair(TokenType.OR, TokenType.ALTER)
1928        )
1929        refresh = self._match_pair(TokenType.OR, TokenType.REFRESH)
1930
1931        unique = self._match(TokenType.UNIQUE)
1932
1933        if self._match_text_seq("CLUSTERED", "COLUMNSTORE"):
1934            clustered = True
1935        elif self._match_text_seq("NONCLUSTERED", "COLUMNSTORE") or self._match_text_seq(
1936            "COLUMNSTORE"
1937        ):
1938            clustered = False
1939        else:
1940            clustered = None
1941
1942        if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False):
1943            self._advance()
1944
1945        properties = None
1946        create_token = self._match_set(self.CREATABLES) and self._prev
1947
1948        if not create_token:
1949            # exp.Properties.Location.POST_CREATE
1950            properties = self._parse_properties()
1951            create_token = self._match_set(self.CREATABLES) and self._prev
1952
1953            if not properties or not create_token:
1954                return self._parse_as_command(start)
1955
1956        concurrently = self._match_text_seq("CONCURRENTLY")
1957        exists = self._parse_exists(not_=True)
1958        this = None
1959        expression: t.Optional[exp.Expression] = None
1960        indexes = None
1961        no_schema_binding = None
1962        begin = None
1963        end = None
1964        clone = None
1965
1966        def extend_props(temp_props: t.Optional[exp.Properties]) -> None:
1967            nonlocal properties
1968            if properties and temp_props:
1969                properties.expressions.extend(temp_props.expressions)
1970            elif temp_props:
1971                properties = temp_props
1972
1973        if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1974            this = self._parse_user_defined_function(kind=create_token.token_type)
1975
1976            # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature)
1977            extend_props(self._parse_properties())
1978
1979            expression = self._match(TokenType.ALIAS) and self._parse_heredoc()
1980            extend_props(self._parse_properties())
1981
1982            if not expression:
1983                if self._match(TokenType.COMMAND):
1984                    expression = self._parse_as_command(self._prev)
1985                else:
1986                    begin = self._match(TokenType.BEGIN)
1987                    return_ = self._match_text_seq("RETURN")
1988
1989                    if self._match(TokenType.STRING, advance=False):
1990                        # Takes care of BigQuery's JavaScript UDF definitions that end in an OPTIONS property
1991                        # # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement
1992                        expression = self._parse_string()
1993                        extend_props(self._parse_properties())
1994                    else:
1995                        expression = self._parse_user_defined_function_expression()
1996
1997                    end = self._match_text_seq("END")
1998
1999                    if return_:
2000                        expression = self.expression(exp.Return, this=expression)
2001        elif create_token.token_type == TokenType.INDEX:
2002            # Postgres allows anonymous indexes, eg. CREATE INDEX IF NOT EXISTS ON t(c)
2003            if not self._match(TokenType.ON):
2004                index = self._parse_id_var()
2005                anonymous = False
2006            else:
2007                index = None
2008                anonymous = True
2009
2010            this = self._parse_index(index=index, anonymous=anonymous)
2011        elif create_token.token_type in self.DB_CREATABLES:
2012            table_parts = self._parse_table_parts(
2013                schema=True, is_db_reference=create_token.token_type == TokenType.SCHEMA
2014            )
2015
2016            # exp.Properties.Location.POST_NAME
2017            self._match(TokenType.COMMA)
2018            extend_props(self._parse_properties(before=True))
2019
2020            this = self._parse_schema(this=table_parts)
2021
2022            # exp.Properties.Location.POST_SCHEMA and POST_WITH
2023            extend_props(self._parse_properties())
2024
2025            has_alias = self._match(TokenType.ALIAS)
2026            if not self._match_set(self.DDL_SELECT_TOKENS, advance=False):
2027                # exp.Properties.Location.POST_ALIAS
2028                extend_props(self._parse_properties())
2029
2030            if create_token.token_type == TokenType.SEQUENCE:
2031                expression = self._parse_types()
2032                extend_props(self._parse_properties())
2033            else:
2034                expression = self._parse_ddl_select()
2035
2036                # Some dialects also support using a table as an alias instead of a SELECT.
2037                # Here we fallback to this as an alternative.
2038                if not expression and has_alias:
2039                    expression = self._try_parse(self._parse_table_parts)
2040
2041            if create_token.token_type == TokenType.TABLE:
2042                # exp.Properties.Location.POST_EXPRESSION
2043                extend_props(self._parse_properties())
2044
2045                indexes = []
2046                while True:
2047                    index = self._parse_index()
2048
2049                    # exp.Properties.Location.POST_INDEX
2050                    extend_props(self._parse_properties())
2051                    if not index:
2052                        break
2053                    else:
2054                        self._match(TokenType.COMMA)
2055                        indexes.append(index)
2056            elif create_token.token_type == TokenType.VIEW:
2057                if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"):
2058                    no_schema_binding = True
2059            elif create_token.token_type in (TokenType.SINK, TokenType.SOURCE):
2060                extend_props(self._parse_properties())
2061
2062            shallow = self._match_text_seq("SHALLOW")
2063
2064            if self._match_texts(self.CLONE_KEYWORDS):
2065                copy = self._prev.text.lower() == "copy"
2066                clone = self.expression(
2067                    exp.Clone, this=self._parse_table(schema=True), shallow=shallow, copy=copy
2068                )
2069
2070        if self._curr and not self._match_set((TokenType.R_PAREN, TokenType.COMMA), advance=False):
2071            return self._parse_as_command(start)
2072
2073        create_kind_text = create_token.text.upper()
2074        return self.expression(
2075            exp.Create,
2076            this=this,
2077            kind=self.dialect.CREATABLE_KIND_MAPPING.get(create_kind_text) or create_kind_text,
2078            replace=replace,
2079            refresh=refresh,
2080            unique=unique,
2081            expression=expression,
2082            exists=exists,
2083            properties=properties,
2084            indexes=indexes,
2085            no_schema_binding=no_schema_binding,
2086            begin=begin,
2087            end=end,
2088            clone=clone,
2089            concurrently=concurrently,
2090            clustered=clustered,
2091        )
2092
2093    def _parse_sequence_properties(self) -> t.Optional[exp.SequenceProperties]:
2094        seq = exp.SequenceProperties()
2095
2096        options = []
2097        index = self._index
2098
2099        while self._curr:
2100            self._match(TokenType.COMMA)
2101            if self._match_text_seq("INCREMENT"):
2102                self._match_text_seq("BY")
2103                self._match_text_seq("=")
2104                seq.set("increment", self._parse_term())
2105            elif self._match_text_seq("MINVALUE"):
2106                seq.set("minvalue", self._parse_term())
2107            elif self._match_text_seq("MAXVALUE"):
2108                seq.set("maxvalue", self._parse_term())
2109            elif self._match(TokenType.START_WITH) or self._match_text_seq("START"):
2110                self._match_text_seq("=")
2111                seq.set("start", self._parse_term())
2112            elif self._match_text_seq("CACHE"):
2113                # T-SQL allows empty CACHE which is initialized dynamically
2114                seq.set("cache", self._parse_number() or True)
2115            elif self._match_text_seq("OWNED", "BY"):
2116                # "OWNED BY NONE" is the default
2117                seq.set("owned", None if self._match_text_seq("NONE") else self._parse_column())
2118            else:
2119                opt = self._parse_var_from_options(self.CREATE_SEQUENCE, raise_unmatched=False)
2120                if opt:
2121                    options.append(opt)
2122                else:
2123                    break
2124
2125        seq.set("options", options if options else None)
2126        return None if self._index == index else seq
2127
2128    def _parse_property_before(self) -> t.Optional[exp.Expression]:
2129        # only used for teradata currently
2130        self._match(TokenType.COMMA)
2131
2132        kwargs = {
2133            "no": self._match_text_seq("NO"),
2134            "dual": self._match_text_seq("DUAL"),
2135            "before": self._match_text_seq("BEFORE"),
2136            "default": self._match_text_seq("DEFAULT"),
2137            "local": (self._match_text_seq("LOCAL") and "LOCAL")
2138            or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"),
2139            "after": self._match_text_seq("AFTER"),
2140            "minimum": self._match_texts(("MIN", "MINIMUM")),
2141            "maximum": self._match_texts(("MAX", "MAXIMUM")),
2142        }
2143
2144        if self._match_texts(self.PROPERTY_PARSERS):
2145            parser = self.PROPERTY_PARSERS[self._prev.text.upper()]
2146            try:
2147                return parser(self, **{k: v for k, v in kwargs.items() if v})
2148            except TypeError:
2149                self.raise_error(f"Cannot parse property '{self._prev.text}'")
2150
2151        return None
2152
2153    def _parse_wrapped_properties(self) -> t.List[exp.Expression]:
2154        return self._parse_wrapped_csv(self._parse_property)
2155
2156    def _parse_property(self) -> t.Optional[exp.Expression]:
2157        if self._match_texts(self.PROPERTY_PARSERS):
2158            return self.PROPERTY_PARSERS[self._prev.text.upper()](self)
2159
2160        if self._match(TokenType.DEFAULT) and self._match_texts(self.PROPERTY_PARSERS):
2161            return self.PROPERTY_PARSERS[self._prev.text.upper()](self, default=True)
2162
2163        if self._match_text_seq("COMPOUND", "SORTKEY"):
2164            return self._parse_sortkey(compound=True)
2165
2166        if self._match_text_seq("SQL", "SECURITY"):
2167            return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
2168
2169        index = self._index
2170        key = self._parse_column()
2171
2172        if not self._match(TokenType.EQ):
2173            self._retreat(index)
2174            return self._parse_sequence_properties()
2175
2176        # Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
2177        if isinstance(key, exp.Column):
2178            key = key.to_dot() if len(key.parts) > 1 else exp.var(key.name)
2179
2180        value = self._parse_bitwise() or self._parse_var(any_token=True)
2181
2182        # Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
2183        if isinstance(value, exp.Column):
2184            value = exp.var(value.name)
2185
2186        return self.expression(exp.Property, this=key, value=value)
2187
2188    def _parse_stored(self) -> t.Union[exp.FileFormatProperty, exp.StorageHandlerProperty]:
2189        if self._match_text_seq("BY"):
2190            return self.expression(exp.StorageHandlerProperty, this=self._parse_var_or_string())
2191
2192        self._match(TokenType.ALIAS)
2193        input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None
2194        output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None
2195
2196        return self.expression(
2197            exp.FileFormatProperty,
2198            this=(
2199                self.expression(
2200                    exp.InputOutputFormat,
2201                    input_format=input_format,
2202                    output_format=output_format,
2203                )
2204                if input_format or output_format
2205                else self._parse_var_or_string() or self._parse_number() or self._parse_id_var()
2206            ),
2207        )
2208
2209    def _parse_unquoted_field(self) -> t.Optional[exp.Expression]:
2210        field = self._parse_field()
2211        if isinstance(field, exp.Identifier) and not field.quoted:
2212            field = exp.var(field)
2213
2214        return field
2215
2216    def _parse_property_assignment(self, exp_class: t.Type[E], **kwargs: t.Any) -> E:
2217        self._match(TokenType.EQ)
2218        self._match(TokenType.ALIAS)
2219
2220        return self.expression(exp_class, this=self._parse_unquoted_field(), **kwargs)
2221
2222    def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]:
2223        properties = []
2224        while True:
2225            if before:
2226                prop = self._parse_property_before()
2227            else:
2228                prop = self._parse_property()
2229            if not prop:
2230                break
2231            for p in ensure_list(prop):
2232                properties.append(p)
2233
2234        if properties:
2235            return self.expression(exp.Properties, expressions=properties)
2236
2237        return None
2238
2239    def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty:
2240        return self.expression(
2241            exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION")
2242        )
2243
2244    def _parse_security(self) -> t.Optional[exp.SecurityProperty]:
2245        if self._match_texts(("DEFINER", "INVOKER")):
2246            security_specifier = self._prev.text.upper()
2247            return self.expression(exp.SecurityProperty, this=security_specifier)
2248        return None
2249
2250    def _parse_settings_property(self) -> exp.SettingsProperty:
2251        return self.expression(
2252            exp.SettingsProperty, expressions=self._parse_csv(self._parse_assignment)
2253        )
2254
2255    def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty:
2256        if self._index >= 2:
2257            pre_volatile_token = self._tokens[self._index - 2]
2258        else:
2259            pre_volatile_token = None
2260
2261        if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS:
2262            return exp.VolatileProperty()
2263
2264        return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE"))
2265
2266    def _parse_retention_period(self) -> exp.Var:
2267        # Parse TSQL's HISTORY_RETENTION_PERIOD: {INFINITE | <number> DAY | DAYS | MONTH ...}
2268        number = self._parse_number()
2269        number_str = f"{number} " if number else ""
2270        unit = self._parse_var(any_token=True)
2271        return exp.var(f"{number_str}{unit}")
2272
2273    def _parse_system_versioning_property(
2274        self, with_: bool = False
2275    ) -> exp.WithSystemVersioningProperty:
2276        self._match(TokenType.EQ)
2277        prop = self.expression(
2278            exp.WithSystemVersioningProperty,
2279            **{  # type: ignore
2280                "on": True,
2281                "with": with_,
2282            },
2283        )
2284
2285        if self._match_text_seq("OFF"):
2286            prop.set("on", False)
2287            return prop
2288
2289        self._match(TokenType.ON)
2290        if self._match(TokenType.L_PAREN):
2291            while self._curr and not self._match(TokenType.R_PAREN):
2292                if self._match_text_seq("HISTORY_TABLE", "="):
2293                    prop.set("this", self._parse_table_parts())
2294                elif self._match_text_seq("DATA_CONSISTENCY_CHECK", "="):
2295                    prop.set("data_consistency", self._advance_any() and self._prev.text.upper())
2296                elif self._match_text_seq("HISTORY_RETENTION_PERIOD", "="):
2297                    prop.set("retention_period", self._parse_retention_period())
2298
2299                self._match(TokenType.COMMA)
2300
2301        return prop
2302
2303    def _parse_data_deletion_property(self) -> exp.DataDeletionProperty:
2304        self._match(TokenType.EQ)
2305        on = self._match_text_seq("ON") or not self._match_text_seq("OFF")
2306        prop = self.expression(exp.DataDeletionProperty, on=on)
2307
2308        if self._match(TokenType.L_PAREN):
2309            while self._curr and not self._match(TokenType.R_PAREN):
2310                if self._match_text_seq("FILTER_COLUMN", "="):
2311                    prop.set("filter_column", self._parse_column())
2312                elif self._match_text_seq("RETENTION_PERIOD", "="):
2313                    prop.set("retention_period", self._parse_retention_period())
2314
2315                self._match(TokenType.COMMA)
2316
2317        return prop
2318
2319    def _parse_distributed_property(self) -> exp.DistributedByProperty:
2320        kind = "HASH"
2321        expressions: t.Optional[t.List[exp.Expression]] = None
2322        if self._match_text_seq("BY", "HASH"):
2323            expressions = self._parse_wrapped_csv(self._parse_id_var)
2324        elif self._match_text_seq("BY", "RANDOM"):
2325            kind = "RANDOM"
2326
2327        # If the BUCKETS keyword is not present, the number of buckets is AUTO
2328        buckets: t.Optional[exp.Expression] = None
2329        if self._match_text_seq("BUCKETS") and not self._match_text_seq("AUTO"):
2330            buckets = self._parse_number()
2331
2332        return self.expression(
2333            exp.DistributedByProperty,
2334            expressions=expressions,
2335            kind=kind,
2336            buckets=buckets,
2337            order=self._parse_order(),
2338        )
2339
2340    def _parse_composite_key_property(self, expr_type: t.Type[E]) -> E:
2341        self._match_text_seq("KEY")
2342        expressions = self._parse_wrapped_id_vars()
2343        return self.expression(expr_type, expressions=expressions)
2344
2345    def _parse_with_property(self) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
2346        if self._match_text_seq("(", "SYSTEM_VERSIONING"):
2347            prop = self._parse_system_versioning_property(with_=True)
2348            self._match_r_paren()
2349            return prop
2350
2351        if self._match(TokenType.L_PAREN, advance=False):
2352            return self._parse_wrapped_properties()
2353
2354        if self._match_text_seq("JOURNAL"):
2355            return self._parse_withjournaltable()
2356
2357        if self._match_texts(self.VIEW_ATTRIBUTES):
2358            return self.expression(exp.ViewAttributeProperty, this=self._prev.text.upper())
2359
2360        if self._match_text_seq("DATA"):
2361            return self._parse_withdata(no=False)
2362        elif self._match_text_seq("NO", "DATA"):
2363            return self._parse_withdata(no=True)
2364
2365        if self._match(TokenType.SERDE_PROPERTIES, advance=False):
2366            return self._parse_serde_properties(with_=True)
2367
2368        if self._match(TokenType.SCHEMA):
2369            return self.expression(
2370                exp.WithSchemaBindingProperty,
2371                this=self._parse_var_from_options(self.SCHEMA_BINDING_OPTIONS),
2372            )
2373
2374        if self._match_texts(self.PROCEDURE_OPTIONS, advance=False):
2375            return self.expression(
2376                exp.WithProcedureOptions, expressions=self._parse_csv(self._parse_procedure_option)
2377            )
2378
2379        if not self._next:
2380            return None
2381
2382        return self._parse_withisolatedloading()
2383
2384    def _parse_procedure_option(self) -> exp.Expression | None:
2385        if self._match_text_seq("EXECUTE", "AS"):
2386            return self.expression(
2387                exp.ExecuteAsProperty,
2388                this=self._parse_var_from_options(self.EXECUTE_AS_OPTIONS, raise_unmatched=False)
2389                or self._parse_string(),
2390            )
2391
2392        return self._parse_var_from_options(self.PROCEDURE_OPTIONS)
2393
2394    # https://dev.mysql.com/doc/refman/8.0/en/create-view.html
2395    def _parse_definer(self) -> t.Optional[exp.DefinerProperty]:
2396        self._match(TokenType.EQ)
2397
2398        user = self._parse_id_var()
2399        self._match(TokenType.PARAMETER)
2400        host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text)
2401
2402        if not user or not host:
2403            return None
2404
2405        return exp.DefinerProperty(this=f"{user}@{host}")
2406
2407    def _parse_withjournaltable(self) -> exp.WithJournalTableProperty:
2408        self._match(TokenType.TABLE)
2409        self._match(TokenType.EQ)
2410        return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts())
2411
2412    def _parse_log(self, no: bool = False) -> exp.LogProperty:
2413        return self.expression(exp.LogProperty, no=no)
2414
2415    def _parse_journal(self, **kwargs) -> exp.JournalProperty:
2416        return self.expression(exp.JournalProperty, **kwargs)
2417
2418    def _parse_checksum(self) -> exp.ChecksumProperty:
2419        self._match(TokenType.EQ)
2420
2421        on = None
2422        if self._match(TokenType.ON):
2423            on = True
2424        elif self._match_text_seq("OFF"):
2425            on = False
2426
2427        return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT))
2428
2429    def _parse_cluster(self, wrapped: bool = False) -> exp.Cluster:
2430        return self.expression(
2431            exp.Cluster,
2432            expressions=(
2433                self._parse_wrapped_csv(self._parse_ordered)
2434                if wrapped
2435                else self._parse_csv(self._parse_ordered)
2436            ),
2437        )
2438
2439    def _parse_clustered_by(self) -> exp.ClusteredByProperty:
2440        self._match_text_seq("BY")
2441
2442        self._match_l_paren()
2443        expressions = self._parse_csv(self._parse_column)
2444        self._match_r_paren()
2445
2446        if self._match_text_seq("SORTED", "BY"):
2447            self._match_l_paren()
2448            sorted_by = self._parse_csv(self._parse_ordered)
2449            self._match_r_paren()
2450        else:
2451            sorted_by = None
2452
2453        self._match(TokenType.INTO)
2454        buckets = self._parse_number()
2455        self._match_text_seq("BUCKETS")
2456
2457        return self.expression(
2458            exp.ClusteredByProperty,
2459            expressions=expressions,
2460            sorted_by=sorted_by,
2461            buckets=buckets,
2462        )
2463
2464    def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]:
2465        if not self._match_text_seq("GRANTS"):
2466            self._retreat(self._index - 1)
2467            return None
2468
2469        return self.expression(exp.CopyGrantsProperty)
2470
2471    def _parse_freespace(self) -> exp.FreespaceProperty:
2472        self._match(TokenType.EQ)
2473        return self.expression(
2474            exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT)
2475        )
2476
2477    def _parse_mergeblockratio(
2478        self, no: bool = False, default: bool = False
2479    ) -> exp.MergeBlockRatioProperty:
2480        if self._match(TokenType.EQ):
2481            return self.expression(
2482                exp.MergeBlockRatioProperty,
2483                this=self._parse_number(),
2484                percent=self._match(TokenType.PERCENT),
2485            )
2486
2487        return self.expression(exp.MergeBlockRatioProperty, no=no, default=default)
2488
2489    def _parse_datablocksize(
2490        self,
2491        default: t.Optional[bool] = None,
2492        minimum: t.Optional[bool] = None,
2493        maximum: t.Optional[bool] = None,
2494    ) -> exp.DataBlocksizeProperty:
2495        self._match(TokenType.EQ)
2496        size = self._parse_number()
2497
2498        units = None
2499        if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")):
2500            units = self._prev.text
2501
2502        return self.expression(
2503            exp.DataBlocksizeProperty,
2504            size=size,
2505            units=units,
2506            default=default,
2507            minimum=minimum,
2508            maximum=maximum,
2509        )
2510
2511    def _parse_blockcompression(self) -> exp.BlockCompressionProperty:
2512        self._match(TokenType.EQ)
2513        always = self._match_text_seq("ALWAYS")
2514        manual = self._match_text_seq("MANUAL")
2515        never = self._match_text_seq("NEVER")
2516        default = self._match_text_seq("DEFAULT")
2517
2518        autotemp = None
2519        if self._match_text_seq("AUTOTEMP"):
2520            autotemp = self._parse_schema()
2521
2522        return self.expression(
2523            exp.BlockCompressionProperty,
2524            always=always,
2525            manual=manual,
2526            never=never,
2527            default=default,
2528            autotemp=autotemp,
2529        )
2530
2531    def _parse_withisolatedloading(self) -> t.Optional[exp.IsolatedLoadingProperty]:
2532        index = self._index
2533        no = self._match_text_seq("NO")
2534        concurrent = self._match_text_seq("CONCURRENT")
2535
2536        if not self._match_text_seq("ISOLATED", "LOADING"):
2537            self._retreat(index)
2538            return None
2539
2540        target = self._parse_var_from_options(self.ISOLATED_LOADING_OPTIONS, raise_unmatched=False)
2541        return self.expression(
2542            exp.IsolatedLoadingProperty, no=no, concurrent=concurrent, target=target
2543        )
2544
2545    def _parse_locking(self) -> exp.LockingProperty:
2546        if self._match(TokenType.TABLE):
2547            kind = "TABLE"
2548        elif self._match(TokenType.VIEW):
2549            kind = "VIEW"
2550        elif self._match(TokenType.ROW):
2551            kind = "ROW"
2552        elif self._match_text_seq("DATABASE"):
2553            kind = "DATABASE"
2554        else:
2555            kind = None
2556
2557        if kind in ("DATABASE", "TABLE", "VIEW"):
2558            this = self._parse_table_parts()
2559        else:
2560            this = None
2561
2562        if self._match(TokenType.FOR):
2563            for_or_in = "FOR"
2564        elif self._match(TokenType.IN):
2565            for_or_in = "IN"
2566        else:
2567            for_or_in = None
2568
2569        if self._match_text_seq("ACCESS"):
2570            lock_type = "ACCESS"
2571        elif self._match_texts(("EXCL", "EXCLUSIVE")):
2572            lock_type = "EXCLUSIVE"
2573        elif self._match_text_seq("SHARE"):
2574            lock_type = "SHARE"
2575        elif self._match_text_seq("READ"):
2576            lock_type = "READ"
2577        elif self._match_text_seq("WRITE"):
2578            lock_type = "WRITE"
2579        elif self._match_text_seq("CHECKSUM"):
2580            lock_type = "CHECKSUM"
2581        else:
2582            lock_type = None
2583
2584        override = self._match_text_seq("OVERRIDE")
2585
2586        return self.expression(
2587            exp.LockingProperty,
2588            this=this,
2589            kind=kind,
2590            for_or_in=for_or_in,
2591            lock_type=lock_type,
2592            override=override,
2593        )
2594
2595    def _parse_partition_by(self) -> t.List[exp.Expression]:
2596        if self._match(TokenType.PARTITION_BY):
2597            return self._parse_csv(self._parse_assignment)
2598        return []
2599
2600    def _parse_partition_bound_spec(self) -> exp.PartitionBoundSpec:
2601        def _parse_partition_bound_expr() -> t.Optional[exp.Expression]:
2602            if self._match_text_seq("MINVALUE"):
2603                return exp.var("MINVALUE")
2604            if self._match_text_seq("MAXVALUE"):
2605                return exp.var("MAXVALUE")
2606            return self._parse_bitwise()
2607
2608        this: t.Optional[exp.Expression | t.List[exp.Expression]] = None
2609        expression = None
2610        from_expressions = None
2611        to_expressions = None
2612
2613        if self._match(TokenType.IN):
2614            this = self._parse_wrapped_csv(self._parse_bitwise)
2615        elif self._match(TokenType.FROM):
2616            from_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2617            self._match_text_seq("TO")
2618            to_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2619        elif self._match_text_seq("WITH", "(", "MODULUS"):
2620            this = self._parse_number()
2621            self._match_text_seq(",", "REMAINDER")
2622            expression = self._parse_number()
2623            self._match_r_paren()
2624        else:
2625            self.raise_error("Failed to parse partition bound spec.")
2626
2627        return self.expression(
2628            exp.PartitionBoundSpec,
2629            this=this,
2630            expression=expression,
2631            from_expressions=from_expressions,
2632            to_expressions=to_expressions,
2633        )
2634
2635    # https://www.postgresql.org/docs/current/sql-createtable.html
2636    def _parse_partitioned_of(self) -> t.Optional[exp.PartitionedOfProperty]:
2637        if not self._match_text_seq("OF"):
2638            self._retreat(self._index - 1)
2639            return None
2640
2641        this = self._parse_table(schema=True)
2642
2643        if self._match(TokenType.DEFAULT):
2644            expression: exp.Var | exp.PartitionBoundSpec = exp.var("DEFAULT")
2645        elif self._match_text_seq("FOR", "VALUES"):
2646            expression = self._parse_partition_bound_spec()
2647        else:
2648            self.raise_error("Expecting either DEFAULT or FOR VALUES clause.")
2649
2650        return self.expression(exp.PartitionedOfProperty, this=this, expression=expression)
2651
2652    def _parse_partitioned_by(self) -> exp.PartitionedByProperty:
2653        self._match(TokenType.EQ)
2654        return self.expression(
2655            exp.PartitionedByProperty,
2656            this=self._parse_schema() or self._parse_bracket(self._parse_field()),
2657        )
2658
2659    def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty:
2660        if self._match_text_seq("AND", "STATISTICS"):
2661            statistics = True
2662        elif self._match_text_seq("AND", "NO", "STATISTICS"):
2663            statistics = False
2664        else:
2665            statistics = None
2666
2667        return self.expression(exp.WithDataProperty, no=no, statistics=statistics)
2668
2669    def _parse_contains_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2670        if self._match_text_seq("SQL"):
2671            return self.expression(exp.SqlReadWriteProperty, this="CONTAINS SQL")
2672        return None
2673
2674    def _parse_modifies_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2675        if self._match_text_seq("SQL", "DATA"):
2676            return self.expression(exp.SqlReadWriteProperty, this="MODIFIES SQL DATA")
2677        return None
2678
2679    def _parse_no_property(self) -> t.Optional[exp.Expression]:
2680        if self._match_text_seq("PRIMARY", "INDEX"):
2681            return exp.NoPrimaryIndexProperty()
2682        if self._match_text_seq("SQL"):
2683            return self.expression(exp.SqlReadWriteProperty, this="NO SQL")
2684        return None
2685
2686    def _parse_on_property(self) -> t.Optional[exp.Expression]:
2687        if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"):
2688            return exp.OnCommitProperty()
2689        if self._match_text_seq("COMMIT", "DELETE", "ROWS"):
2690            return exp.OnCommitProperty(delete=True)
2691        return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var()))
2692
2693    def _parse_reads_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2694        if self._match_text_seq("SQL", "DATA"):
2695            return self.expression(exp.SqlReadWriteProperty, this="READS SQL DATA")
2696        return None
2697
2698    def _parse_distkey(self) -> exp.DistKeyProperty:
2699        return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var))
2700
2701    def _parse_create_like(self) -> t.Optional[exp.LikeProperty]:
2702        table = self._parse_table(schema=True)
2703
2704        options = []
2705        while self._match_texts(("INCLUDING", "EXCLUDING")):
2706            this = self._prev.text.upper()
2707
2708            id_var = self._parse_id_var()
2709            if not id_var:
2710                return None
2711
2712            options.append(
2713                self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper()))
2714            )
2715
2716        return self.expression(exp.LikeProperty, this=table, expressions=options)
2717
2718    def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty:
2719        return self.expression(
2720            exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound
2721        )
2722
2723    def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty:
2724        self._match(TokenType.EQ)
2725        return self.expression(
2726            exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default
2727        )
2728
2729    def _parse_remote_with_connection(self) -> exp.RemoteWithConnectionModelProperty:
2730        self._match_text_seq("WITH", "CONNECTION")
2731        return self.expression(
2732            exp.RemoteWithConnectionModelProperty, this=self._parse_table_parts()
2733        )
2734
2735    def _parse_returns(self) -> exp.ReturnsProperty:
2736        value: t.Optional[exp.Expression]
2737        null = None
2738        is_table = self._match(TokenType.TABLE)
2739
2740        if is_table:
2741            if self._match(TokenType.LT):
2742                value = self.expression(
2743                    exp.Schema,
2744                    this="TABLE",
2745                    expressions=self._parse_csv(self._parse_struct_types),
2746                )
2747                if not self._match(TokenType.GT):
2748                    self.raise_error("Expecting >")
2749            else:
2750                value = self._parse_schema(exp.var("TABLE"))
2751        elif self._match_text_seq("NULL", "ON", "NULL", "INPUT"):
2752            null = True
2753            value = None
2754        else:
2755            value = self._parse_types()
2756
2757        return self.expression(exp.ReturnsProperty, this=value, is_table=is_table, null=null)
2758
2759    def _parse_describe(self) -> exp.Describe:
2760        kind = self._match_set(self.CREATABLES) and self._prev.text
2761        style = self._match_texts(self.DESCRIBE_STYLES) and self._prev.text.upper()
2762        if self._match(TokenType.DOT):
2763            style = None
2764            self._retreat(self._index - 2)
2765
2766        format = self._parse_property() if self._match(TokenType.FORMAT, advance=False) else None
2767
2768        if self._match_set(self.STATEMENT_PARSERS, advance=False):
2769            this = self._parse_statement()
2770        else:
2771            this = self._parse_table(schema=True)
2772
2773        properties = self._parse_properties()
2774        expressions = properties.expressions if properties else None
2775        partition = self._parse_partition()
2776        return self.expression(
2777            exp.Describe,
2778            this=this,
2779            style=style,
2780            kind=kind,
2781            expressions=expressions,
2782            partition=partition,
2783            format=format,
2784        )
2785
2786    def _parse_multitable_inserts(self, comments: t.Optional[t.List[str]]) -> exp.MultitableInserts:
2787        kind = self._prev.text.upper()
2788        expressions = []
2789
2790        def parse_conditional_insert() -> t.Optional[exp.ConditionalInsert]:
2791            if self._match(TokenType.WHEN):
2792                expression = self._parse_disjunction()
2793                self._match(TokenType.THEN)
2794            else:
2795                expression = None
2796
2797            else_ = self._match(TokenType.ELSE)
2798
2799            if not self._match(TokenType.INTO):
2800                return None
2801
2802            return self.expression(
2803                exp.ConditionalInsert,
2804                this=self.expression(
2805                    exp.Insert,
2806                    this=self._parse_table(schema=True),
2807                    expression=self._parse_derived_table_values(),
2808                ),
2809                expression=expression,
2810                else_=else_,
2811            )
2812
2813        expression = parse_conditional_insert()
2814        while expression is not None:
2815            expressions.append(expression)
2816            expression = parse_conditional_insert()
2817
2818        return self.expression(
2819            exp.MultitableInserts,
2820            kind=kind,
2821            comments=comments,
2822            expressions=expressions,
2823            source=self._parse_table(),
2824        )
2825
2826    def _parse_insert(self) -> t.Union[exp.Insert, exp.MultitableInserts]:
2827        comments = []
2828        hint = self._parse_hint()
2829        overwrite = self._match(TokenType.OVERWRITE)
2830        ignore = self._match(TokenType.IGNORE)
2831        local = self._match_text_seq("LOCAL")
2832        alternative = None
2833        is_function = None
2834
2835        if self._match_text_seq("DIRECTORY"):
2836            this: t.Optional[exp.Expression] = self.expression(
2837                exp.Directory,
2838                this=self._parse_var_or_string(),
2839                local=local,
2840                row_format=self._parse_row_format(match_row=True),
2841            )
2842        else:
2843            if self._match_set((TokenType.FIRST, TokenType.ALL)):
2844                comments += ensure_list(self._prev_comments)
2845                return self._parse_multitable_inserts(comments)
2846
2847            if self._match(TokenType.OR):
2848                alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text
2849
2850            self._match(TokenType.INTO)
2851            comments += ensure_list(self._prev_comments)
2852            self._match(TokenType.TABLE)
2853            is_function = self._match(TokenType.FUNCTION)
2854
2855            this = (
2856                self._parse_table(schema=True, parse_partition=True)
2857                if not is_function
2858                else self._parse_function()
2859            )
2860            if isinstance(this, exp.Table) and self._match(TokenType.ALIAS, advance=False):
2861                this.set("alias", self._parse_table_alias())
2862
2863        returning = self._parse_returning()
2864
2865        return self.expression(
2866            exp.Insert,
2867            comments=comments,
2868            hint=hint,
2869            is_function=is_function,
2870            this=this,
2871            stored=self._match_text_seq("STORED") and self._parse_stored(),
2872            by_name=self._match_text_seq("BY", "NAME"),
2873            exists=self._parse_exists(),
2874            where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) and self._parse_assignment(),
2875            partition=self._match(TokenType.PARTITION_BY) and self._parse_partitioned_by(),
2876            settings=self._match_text_seq("SETTINGS") and self._parse_settings_property(),
2877            expression=self._parse_derived_table_values() or self._parse_ddl_select(),
2878            conflict=self._parse_on_conflict(),
2879            returning=returning or self._parse_returning(),
2880            overwrite=overwrite,
2881            alternative=alternative,
2882            ignore=ignore,
2883            source=self._match(TokenType.TABLE) and self._parse_table(),
2884        )
2885
2886    def _parse_kill(self) -> exp.Kill:
2887        kind = exp.var(self._prev.text) if self._match_texts(("CONNECTION", "QUERY")) else None
2888
2889        return self.expression(
2890            exp.Kill,
2891            this=self._parse_primary(),
2892            kind=kind,
2893        )
2894
2895    def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]:
2896        conflict = self._match_text_seq("ON", "CONFLICT")
2897        duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY")
2898
2899        if not conflict and not duplicate:
2900            return None
2901
2902        conflict_keys = None
2903        constraint = None
2904
2905        if conflict:
2906            if self._match_text_seq("ON", "CONSTRAINT"):
2907                constraint = self._parse_id_var()
2908            elif self._match(TokenType.L_PAREN):
2909                conflict_keys = self._parse_csv(self._parse_id_var)
2910                self._match_r_paren()
2911
2912        action = self._parse_var_from_options(self.CONFLICT_ACTIONS)
2913        if self._prev.token_type == TokenType.UPDATE:
2914            self._match(TokenType.SET)
2915            expressions = self._parse_csv(self._parse_equality)
2916        else:
2917            expressions = None
2918
2919        return self.expression(
2920            exp.OnConflict,
2921            duplicate=duplicate,
2922            expressions=expressions,
2923            action=action,
2924            conflict_keys=conflict_keys,
2925            constraint=constraint,
2926            where=self._parse_where(),
2927        )
2928
2929    def _parse_returning(self) -> t.Optional[exp.Returning]:
2930        if not self._match(TokenType.RETURNING):
2931            return None
2932        return self.expression(
2933            exp.Returning,
2934            expressions=self._parse_csv(self._parse_expression),
2935            into=self._match(TokenType.INTO) and self._parse_table_part(),
2936        )
2937
2938    def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2939        if not self._match(TokenType.FORMAT):
2940            return None
2941        return self._parse_row_format()
2942
2943    def _parse_serde_properties(self, with_: bool = False) -> t.Optional[exp.SerdeProperties]:
2944        index = self._index
2945        with_ = with_ or self._match_text_seq("WITH")
2946
2947        if not self._match(TokenType.SERDE_PROPERTIES):
2948            self._retreat(index)
2949            return None
2950        return self.expression(
2951            exp.SerdeProperties,
2952            **{  # type: ignore
2953                "expressions": self._parse_wrapped_properties(),
2954                "with": with_,
2955            },
2956        )
2957
2958    def _parse_row_format(
2959        self, match_row: bool = False
2960    ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2961        if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT):
2962            return None
2963
2964        if self._match_text_seq("SERDE"):
2965            this = self._parse_string()
2966
2967            serde_properties = self._parse_serde_properties()
2968
2969            return self.expression(
2970                exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties
2971            )
2972
2973        self._match_text_seq("DELIMITED")
2974
2975        kwargs = {}
2976
2977        if self._match_text_seq("FIELDS", "TERMINATED", "BY"):
2978            kwargs["fields"] = self._parse_string()
2979            if self._match_text_seq("ESCAPED", "BY"):
2980                kwargs["escaped"] = self._parse_string()
2981        if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"):
2982            kwargs["collection_items"] = self._parse_string()
2983        if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"):
2984            kwargs["map_keys"] = self._parse_string()
2985        if self._match_text_seq("LINES", "TERMINATED", "BY"):
2986            kwargs["lines"] = self._parse_string()
2987        if self._match_text_seq("NULL", "DEFINED", "AS"):
2988            kwargs["null"] = self._parse_string()
2989
2990        return self.expression(exp.RowFormatDelimitedProperty, **kwargs)  # type: ignore
2991
2992    def _parse_load(self) -> exp.LoadData | exp.Command:
2993        if self._match_text_seq("DATA"):
2994            local = self._match_text_seq("LOCAL")
2995            self._match_text_seq("INPATH")
2996            inpath = self._parse_string()
2997            overwrite = self._match(TokenType.OVERWRITE)
2998            self._match_pair(TokenType.INTO, TokenType.TABLE)
2999
3000            return self.expression(
3001                exp.LoadData,
3002                this=self._parse_table(schema=True),
3003                local=local,
3004                overwrite=overwrite,
3005                inpath=inpath,
3006                partition=self._parse_partition(),
3007                input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(),
3008                serde=self._match_text_seq("SERDE") and self._parse_string(),
3009            )
3010        return self._parse_as_command(self._prev)
3011
3012    def _parse_delete(self) -> exp.Delete:
3013        # This handles MySQL's "Multiple-Table Syntax"
3014        # https://dev.mysql.com/doc/refman/8.0/en/delete.html
3015        tables = None
3016        if not self._match(TokenType.FROM, advance=False):
3017            tables = self._parse_csv(self._parse_table) or None
3018
3019        returning = self._parse_returning()
3020
3021        return self.expression(
3022            exp.Delete,
3023            tables=tables,
3024            this=self._match(TokenType.FROM) and self._parse_table(joins=True),
3025            using=self._match(TokenType.USING) and self._parse_table(joins=True),
3026            cluster=self._match(TokenType.ON) and self._parse_on_property(),
3027            where=self._parse_where(),
3028            returning=returning or self._parse_returning(),
3029            limit=self._parse_limit(),
3030        )
3031
3032    def _parse_update(self) -> exp.Update:
3033        this = self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS)
3034        expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality)
3035        returning = self._parse_returning()
3036        return self.expression(
3037            exp.Update,
3038            **{  # type: ignore
3039                "this": this,
3040                "expressions": expressions,
3041                "from": self._parse_from(joins=True),
3042                "where": self._parse_where(),
3043                "returning": returning or self._parse_returning(),
3044                "order": self._parse_order(),
3045                "limit": self._parse_limit(),
3046            },
3047        )
3048
3049    def _parse_use(self) -> exp.Use:
3050        return self.expression(
3051            exp.Use,
3052            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
3053            this=self._parse_table(schema=False),
3054        )
3055
3056    def _parse_uncache(self) -> exp.Uncache:
3057        if not self._match(TokenType.TABLE):
3058            self.raise_error("Expecting TABLE after UNCACHE")
3059
3060        return self.expression(
3061            exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True)
3062        )
3063
3064    def _parse_cache(self) -> exp.Cache:
3065        lazy = self._match_text_seq("LAZY")
3066        self._match(TokenType.TABLE)
3067        table = self._parse_table(schema=True)
3068
3069        options = []
3070        if self._match_text_seq("OPTIONS"):
3071            self._match_l_paren()
3072            k = self._parse_string()
3073            self._match(TokenType.EQ)
3074            v = self._parse_string()
3075            options = [k, v]
3076            self._match_r_paren()
3077
3078        self._match(TokenType.ALIAS)
3079        return self.expression(
3080            exp.Cache,
3081            this=table,
3082            lazy=lazy,
3083            options=options,
3084            expression=self._parse_select(nested=True),
3085        )
3086
3087    def _parse_partition(self) -> t.Optional[exp.Partition]:
3088        if not self._match_texts(self.PARTITION_KEYWORDS):
3089            return None
3090
3091        return self.expression(
3092            exp.Partition,
3093            subpartition=self._prev.text.upper() == "SUBPARTITION",
3094            expressions=self._parse_wrapped_csv(self._parse_assignment),
3095        )
3096
3097    def _parse_value(self, values: bool = True) -> t.Optional[exp.Tuple]:
3098        def _parse_value_expression() -> t.Optional[exp.Expression]:
3099            if self.dialect.SUPPORTS_VALUES_DEFAULT and self._match(TokenType.DEFAULT):
3100                return exp.var(self._prev.text.upper())
3101            return self._parse_expression()
3102
3103        if self._match(TokenType.L_PAREN):
3104            expressions = self._parse_csv(_parse_value_expression)
3105            self._match_r_paren()
3106            return self.expression(exp.Tuple, expressions=expressions)
3107
3108        # In some dialects we can have VALUES 1, 2 which results in 1 column & 2 rows.
3109        expression = self._parse_expression()
3110        if expression:
3111            return self.expression(exp.Tuple, expressions=[expression])
3112        return None
3113
3114    def _parse_projections(self) -> t.List[exp.Expression]:
3115        return self._parse_expressions()
3116
3117    def _parse_wrapped_select(self, table: bool = False) -> t.Optional[exp.Expression]:
3118        if self._match_set((TokenType.PIVOT, TokenType.UNPIVOT)):
3119            this: t.Optional[exp.Expression] = self._parse_simplified_pivot(
3120                is_unpivot=self._prev.token_type == TokenType.UNPIVOT
3121            )
3122        elif self._match(TokenType.FROM):
3123            from_ = self._parse_from(skip_from_token=True)
3124            # Support parentheses for duckdb FROM-first syntax
3125            select = self._parse_select()
3126            if select:
3127                select.set("from", from_)
3128                this = select
3129            else:
3130                this = exp.select("*").from_(t.cast(exp.From, from_))
3131        else:
3132            this = (
3133                self._parse_table()
3134                if table
3135                else self._parse_select(nested=True, parse_set_operation=False)
3136            )
3137
3138            # Transform exp.Values into a exp.Table to pass through parse_query_modifiers
3139            # in case a modifier (e.g. join) is following
3140            if table and isinstance(this, exp.Values) and this.alias:
3141                alias = this.args["alias"].pop()
3142                this = exp.Table(this=this, alias=alias)
3143
3144            this = self._parse_query_modifiers(self._parse_set_operations(this))
3145
3146        return this
3147
3148    def _parse_select(
3149        self,
3150        nested: bool = False,
3151        table: bool = False,
3152        parse_subquery_alias: bool = True,
3153        parse_set_operation: bool = True,
3154    ) -> t.Optional[exp.Expression]:
3155        cte = self._parse_with()
3156
3157        if cte:
3158            this = self._parse_statement()
3159
3160            if not this:
3161                self.raise_error("Failed to parse any statement following CTE")
3162                return cte
3163
3164            if "with" in this.arg_types:
3165                this.set("with", cte)
3166            else:
3167                self.raise_error(f"{this.key} does not support CTE")
3168                this = cte
3169
3170            return this
3171
3172        # duckdb supports leading with FROM x
3173        from_ = self._parse_from() if self._match(TokenType.FROM, advance=False) else None
3174
3175        if self._match(TokenType.SELECT):
3176            comments = self._prev_comments
3177
3178            hint = self._parse_hint()
3179
3180            if self._next and not self._next.token_type == TokenType.DOT:
3181                all_ = self._match(TokenType.ALL)
3182                distinct = self._match_set(self.DISTINCT_TOKENS)
3183            else:
3184                all_, distinct = None, None
3185
3186            kind = (
3187                self._match(TokenType.ALIAS)
3188                and self._match_texts(("STRUCT", "VALUE"))
3189                and self._prev.text.upper()
3190            )
3191
3192            if distinct:
3193                distinct = self.expression(
3194                    exp.Distinct,
3195                    on=self._parse_value(values=False) if self._match(TokenType.ON) else None,
3196                )
3197
3198            if all_ and distinct:
3199                self.raise_error("Cannot specify both ALL and DISTINCT after SELECT")
3200
3201            operation_modifiers = []
3202            while self._curr and self._match_texts(self.OPERATION_MODIFIERS):
3203                operation_modifiers.append(exp.var(self._prev.text.upper()))
3204
3205            limit = self._parse_limit(top=True)
3206            projections = self._parse_projections()
3207
3208            this = self.expression(
3209                exp.Select,
3210                kind=kind,
3211                hint=hint,
3212                distinct=distinct,
3213                expressions=projections,
3214                limit=limit,
3215                operation_modifiers=operation_modifiers or None,
3216            )
3217            this.comments = comments
3218
3219            into = self._parse_into()
3220            if into:
3221                this.set("into", into)
3222
3223            if not from_:
3224                from_ = self._parse_from()
3225
3226            if from_:
3227                this.set("from", from_)
3228
3229            this = self._parse_query_modifiers(this)
3230        elif (table or nested) and self._match(TokenType.L_PAREN):
3231            this = self._parse_wrapped_select(table=table)
3232
3233            # We return early here so that the UNION isn't attached to the subquery by the
3234            # following call to _parse_set_operations, but instead becomes the parent node
3235            self._match_r_paren()
3236            return self._parse_subquery(this, parse_alias=parse_subquery_alias)
3237        elif self._match(TokenType.VALUES, advance=False):
3238            this = self._parse_derived_table_values()
3239        elif from_:
3240            this = exp.select("*").from_(from_.this, copy=False)
3241        elif self._match(TokenType.SUMMARIZE):
3242            table = self._match(TokenType.TABLE)
3243            this = self._parse_select() or self._parse_string() or self._parse_table()
3244            return self.expression(exp.Summarize, this=this, table=table)
3245        elif self._match(TokenType.DESCRIBE):
3246            this = self._parse_describe()
3247        elif self._match_text_seq("STREAM"):
3248            this = self._parse_function()
3249            if this:
3250                this = self.expression(exp.Stream, this=this)
3251            else:
3252                self._retreat(self._index - 1)
3253        else:
3254            this = None
3255
3256        return self._parse_set_operations(this) if parse_set_operation else this
3257
3258    def _parse_recursive_with_search(self) -> t.Optional[exp.RecursiveWithSearch]:
3259        self._match_text_seq("SEARCH")
3260
3261        kind = self._match_texts(self.RECURSIVE_CTE_SEARCH_KIND) and self._prev.text.upper()
3262
3263        if not kind:
3264            return None
3265
3266        self._match_text_seq("FIRST", "BY")
3267
3268        return self.expression(
3269            exp.RecursiveWithSearch,
3270            kind=kind,
3271            this=self._parse_id_var(),
3272            expression=self._match_text_seq("SET") and self._parse_id_var(),
3273            using=self._match_text_seq("USING") and self._parse_id_var(),
3274        )
3275
3276    def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]:
3277        if not skip_with_token and not self._match(TokenType.WITH):
3278            return None
3279
3280        comments = self._prev_comments
3281        recursive = self._match(TokenType.RECURSIVE)
3282
3283        last_comments = None
3284        expressions = []
3285        while True:
3286            cte = self._parse_cte()
3287            if isinstance(cte, exp.CTE):
3288                expressions.append(cte)
3289                if last_comments:
3290                    cte.add_comments(last_comments)
3291
3292            if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH):
3293                break
3294            else:
3295                self._match(TokenType.WITH)
3296
3297            last_comments = self._prev_comments
3298
3299        return self.expression(
3300            exp.With,
3301            comments=comments,
3302            expressions=expressions,
3303            recursive=recursive,
3304            search=self._parse_recursive_with_search(),
3305        )
3306
3307    def _parse_cte(self) -> t.Optional[exp.CTE]:
3308        index = self._index
3309
3310        alias = self._parse_table_alias(self.ID_VAR_TOKENS)
3311        if not alias or not alias.this:
3312            self.raise_error("Expected CTE to have alias")
3313
3314        if not self._match(TokenType.ALIAS) and not self.OPTIONAL_ALIAS_TOKEN_CTE:
3315            self._retreat(index)
3316            return None
3317
3318        comments = self._prev_comments
3319
3320        if self._match_text_seq("NOT", "MATERIALIZED"):
3321            materialized = False
3322        elif self._match_text_seq("MATERIALIZED"):
3323            materialized = True
3324        else:
3325            materialized = None
3326
3327        cte = self.expression(
3328            exp.CTE,
3329            this=self._parse_wrapped(self._parse_statement),
3330            alias=alias,
3331            materialized=materialized,
3332            comments=comments,
3333        )
3334
3335        if isinstance(cte.this, exp.Values):
3336            cte.set("this", exp.select("*").from_(exp.alias_(cte.this, "_values", table=True)))
3337
3338        return cte
3339
3340    def _parse_table_alias(
3341        self, alias_tokens: t.Optional[t.Collection[TokenType]] = None
3342    ) -> t.Optional[exp.TableAlias]:
3343        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
3344        # so this section tries to parse the clause version and if it fails, it treats the token
3345        # as an identifier (alias)
3346        if self._can_parse_limit_or_offset():
3347            return None
3348
3349        any_token = self._match(TokenType.ALIAS)
3350        alias = (
3351            self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3352            or self._parse_string_as_identifier()
3353        )
3354
3355        index = self._index
3356        if self._match(TokenType.L_PAREN):
3357            columns = self._parse_csv(self._parse_function_parameter)
3358            self._match_r_paren() if columns else self._retreat(index)
3359        else:
3360            columns = None
3361
3362        if not alias and not columns:
3363            return None
3364
3365        table_alias = self.expression(exp.TableAlias, this=alias, columns=columns)
3366
3367        # We bubble up comments from the Identifier to the TableAlias
3368        if isinstance(alias, exp.Identifier):
3369            table_alias.add_comments(alias.pop_comments())
3370
3371        return table_alias
3372
3373    def _parse_subquery(
3374        self, this: t.Optional[exp.Expression], parse_alias: bool = True
3375    ) -> t.Optional[exp.Subquery]:
3376        if not this:
3377            return None
3378
3379        return self.expression(
3380            exp.Subquery,
3381            this=this,
3382            pivots=self._parse_pivots(),
3383            alias=self._parse_table_alias() if parse_alias else None,
3384            sample=self._parse_table_sample(),
3385        )
3386
3387    def _implicit_unnests_to_explicit(self, this: E) -> E:
3388        from sqlglot.optimizer.normalize_identifiers import normalize_identifiers as _norm
3389
3390        refs = {_norm(this.args["from"].this.copy(), dialect=self.dialect).alias_or_name}
3391        for i, join in enumerate(this.args.get("joins") or []):
3392            table = join.this
3393            normalized_table = table.copy()
3394            normalized_table.meta["maybe_column"] = True
3395            normalized_table = _norm(normalized_table, dialect=self.dialect)
3396
3397            if isinstance(table, exp.Table) and not join.args.get("on"):
3398                if normalized_table.parts[0].name in refs:
3399                    table_as_column = table.to_column()
3400                    unnest = exp.Unnest(expressions=[table_as_column])
3401
3402                    # Table.to_column creates a parent Alias node that we want to convert to
3403                    # a TableAlias and attach to the Unnest, so it matches the parser's output
3404                    if isinstance(table.args.get("alias"), exp.TableAlias):
3405                        table_as_column.replace(table_as_column.this)
3406                        exp.alias_(unnest, None, table=[table.args["alias"].this], copy=False)
3407
3408                    table.replace(unnest)
3409
3410            refs.add(normalized_table.alias_or_name)
3411
3412        return this
3413
3414    def _parse_query_modifiers(
3415        self, this: t.Optional[exp.Expression]
3416    ) -> t.Optional[exp.Expression]:
3417        if isinstance(this, self.MODIFIABLES):
3418            for join in self._parse_joins():
3419                this.append("joins", join)
3420            for lateral in iter(self._parse_lateral, None):
3421                this.append("laterals", lateral)
3422
3423            while True:
3424                if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False):
3425                    parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type]
3426                    key, expression = parser(self)
3427
3428                    if expression:
3429                        this.set(key, expression)
3430                        if key == "limit":
3431                            offset = expression.args.pop("offset", None)
3432
3433                            if offset:
3434                                offset = exp.Offset(expression=offset)
3435                                this.set("offset", offset)
3436
3437                                limit_by_expressions = expression.expressions
3438                                expression.set("expressions", None)
3439                                offset.set("expressions", limit_by_expressions)
3440                        continue
3441                break
3442
3443        if self.SUPPORTS_IMPLICIT_UNNEST and this and this.args.get("from"):
3444            this = self._implicit_unnests_to_explicit(this)
3445
3446        return this
3447
3448    def _parse_hint_fallback_to_string(self) -> t.Optional[exp.Hint]:
3449        start = self._curr
3450        while self._curr:
3451            self._advance()
3452
3453        end = self._tokens[self._index - 1]
3454        return exp.Hint(expressions=[self._find_sql(start, end)])
3455
3456    def _parse_hint_function_call(self) -> t.Optional[exp.Expression]:
3457        return self._parse_function_call()
3458
3459    def _parse_hint_body(self) -> t.Optional[exp.Hint]:
3460        start_index = self._index
3461        should_fallback_to_string = False
3462
3463        hints = []
3464        try:
3465            for hint in iter(
3466                lambda: self._parse_csv(
3467                    lambda: self._parse_hint_function_call() or self._parse_var(upper=True),
3468                ),
3469                [],
3470            ):
3471                hints.extend(hint)
3472        except ParseError:
3473            should_fallback_to_string = True
3474
3475        if should_fallback_to_string or self._curr:
3476            self._retreat(start_index)
3477            return self._parse_hint_fallback_to_string()
3478
3479        return self.expression(exp.Hint, expressions=hints)
3480
3481    def _parse_hint(self) -> t.Optional[exp.Hint]:
3482        if self._match(TokenType.HINT) and self._prev_comments:
3483            return exp.maybe_parse(self._prev_comments[0], into=exp.Hint, dialect=self.dialect)
3484
3485        return None
3486
3487    def _parse_into(self) -> t.Optional[exp.Into]:
3488        if not self._match(TokenType.INTO):
3489            return None
3490
3491        temp = self._match(TokenType.TEMPORARY)
3492        unlogged = self._match_text_seq("UNLOGGED")
3493        self._match(TokenType.TABLE)
3494
3495        return self.expression(
3496            exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged
3497        )
3498
3499    def _parse_from(
3500        self, joins: bool = False, skip_from_token: bool = False
3501    ) -> t.Optional[exp.From]:
3502        if not skip_from_token and not self._match(TokenType.FROM):
3503            return None
3504
3505        return self.expression(
3506            exp.From, comments=self._prev_comments, this=self._parse_table(joins=joins)
3507        )
3508
3509    def _parse_match_recognize_measure(self) -> exp.MatchRecognizeMeasure:
3510        return self.expression(
3511            exp.MatchRecognizeMeasure,
3512            window_frame=self._match_texts(("FINAL", "RUNNING")) and self._prev.text.upper(),
3513            this=self._parse_expression(),
3514        )
3515
3516    def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]:
3517        if not self._match(TokenType.MATCH_RECOGNIZE):
3518            return None
3519
3520        self._match_l_paren()
3521
3522        partition = self._parse_partition_by()
3523        order = self._parse_order()
3524
3525        measures = (
3526            self._parse_csv(self._parse_match_recognize_measure)
3527            if self._match_text_seq("MEASURES")
3528            else None
3529        )
3530
3531        if self._match_text_seq("ONE", "ROW", "PER", "MATCH"):
3532            rows = exp.var("ONE ROW PER MATCH")
3533        elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"):
3534            text = "ALL ROWS PER MATCH"
3535            if self._match_text_seq("SHOW", "EMPTY", "MATCHES"):
3536                text += " SHOW EMPTY MATCHES"
3537            elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"):
3538                text += " OMIT EMPTY MATCHES"
3539            elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"):
3540                text += " WITH UNMATCHED ROWS"
3541            rows = exp.var(text)
3542        else:
3543            rows = None
3544
3545        if self._match_text_seq("AFTER", "MATCH", "SKIP"):
3546            text = "AFTER MATCH SKIP"
3547            if self._match_text_seq("PAST", "LAST", "ROW"):
3548                text += " PAST LAST ROW"
3549            elif self._match_text_seq("TO", "NEXT", "ROW"):
3550                text += " TO NEXT ROW"
3551            elif self._match_text_seq("TO", "FIRST"):
3552                text += f" TO FIRST {self._advance_any().text}"  # type: ignore
3553            elif self._match_text_seq("TO", "LAST"):
3554                text += f" TO LAST {self._advance_any().text}"  # type: ignore
3555            after = exp.var(text)
3556        else:
3557            after = None
3558
3559        if self._match_text_seq("PATTERN"):
3560            self._match_l_paren()
3561
3562            if not self._curr:
3563                self.raise_error("Expecting )", self._curr)
3564
3565            paren = 1
3566            start = self._curr
3567
3568            while self._curr and paren > 0:
3569                if self._curr.token_type == TokenType.L_PAREN:
3570                    paren += 1
3571                if self._curr.token_type == TokenType.R_PAREN:
3572                    paren -= 1
3573
3574                end = self._prev
3575                self._advance()
3576
3577            if paren > 0:
3578                self.raise_error("Expecting )", self._curr)
3579
3580            pattern = exp.var(self._find_sql(start, end))
3581        else:
3582            pattern = None
3583
3584        define = (
3585            self._parse_csv(self._parse_name_as_expression)
3586            if self._match_text_seq("DEFINE")
3587            else None
3588        )
3589
3590        self._match_r_paren()
3591
3592        return self.expression(
3593            exp.MatchRecognize,
3594            partition_by=partition,
3595            order=order,
3596            measures=measures,
3597            rows=rows,
3598            after=after,
3599            pattern=pattern,
3600            define=define,
3601            alias=self._parse_table_alias(),
3602        )
3603
3604    def _parse_lateral(self) -> t.Optional[exp.Lateral]:
3605        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY)
3606        if not cross_apply and self._match_pair(TokenType.OUTER, TokenType.APPLY):
3607            cross_apply = False
3608
3609        if cross_apply is not None:
3610            this = self._parse_select(table=True)
3611            view = None
3612            outer = None
3613        elif self._match(TokenType.LATERAL):
3614            this = self._parse_select(table=True)
3615            view = self._match(TokenType.VIEW)
3616            outer = self._match(TokenType.OUTER)
3617        else:
3618            return None
3619
3620        if not this:
3621            this = (
3622                self._parse_unnest()
3623                or self._parse_function()
3624                or self._parse_id_var(any_token=False)
3625            )
3626
3627            while self._match(TokenType.DOT):
3628                this = exp.Dot(
3629                    this=this,
3630                    expression=self._parse_function() or self._parse_id_var(any_token=False),
3631                )
3632
3633        ordinality: t.Optional[bool] = None
3634
3635        if view:
3636            table = self._parse_id_var(any_token=False)
3637            columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else []
3638            table_alias: t.Optional[exp.TableAlias] = self.expression(
3639                exp.TableAlias, this=table, columns=columns
3640            )
3641        elif isinstance(this, (exp.Subquery, exp.Unnest)) and this.alias:
3642            # We move the alias from the lateral's child node to the lateral itself
3643            table_alias = this.args["alias"].pop()
3644        else:
3645            ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
3646            table_alias = self._parse_table_alias()
3647
3648        return self.expression(
3649            exp.Lateral,
3650            this=this,
3651            view=view,
3652            outer=outer,
3653            alias=table_alias,
3654            cross_apply=cross_apply,
3655            ordinality=ordinality,
3656        )
3657
3658    def _parse_join_parts(
3659        self,
3660    ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]:
3661        return (
3662            self._match_set(self.JOIN_METHODS) and self._prev,
3663            self._match_set(self.JOIN_SIDES) and self._prev,
3664            self._match_set(self.JOIN_KINDS) and self._prev,
3665        )
3666
3667    def _parse_using_identifiers(self) -> t.List[exp.Expression]:
3668        def _parse_column_as_identifier() -> t.Optional[exp.Expression]:
3669            this = self._parse_column()
3670            if isinstance(this, exp.Column):
3671                return this.this
3672            return this
3673
3674        return self._parse_wrapped_csv(_parse_column_as_identifier, optional=True)
3675
3676    def _parse_join(
3677        self, skip_join_token: bool = False, parse_bracket: bool = False
3678    ) -> t.Optional[exp.Join]:
3679        if self._match(TokenType.COMMA):
3680            table = self._try_parse(self._parse_table)
3681            if table:
3682                return self.expression(exp.Join, this=table)
3683            return None
3684
3685        index = self._index
3686        method, side, kind = self._parse_join_parts()
3687        hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None
3688        join = self._match(TokenType.JOIN) or (kind and kind.token_type == TokenType.STRAIGHT_JOIN)
3689
3690        if not skip_join_token and not join:
3691            self._retreat(index)
3692            kind = None
3693            method = None
3694            side = None
3695
3696        outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False)
3697        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False)
3698
3699        if not skip_join_token and not join and not outer_apply and not cross_apply:
3700            return None
3701
3702        kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)}
3703        if kind and kind.token_type == TokenType.ARRAY and self._match(TokenType.COMMA):
3704            kwargs["expressions"] = self._parse_csv(
3705                lambda: self._parse_table(parse_bracket=parse_bracket)
3706            )
3707
3708        if method:
3709            kwargs["method"] = method.text
3710        if side:
3711            kwargs["side"] = side.text
3712        if kind:
3713            kwargs["kind"] = kind.text
3714        if hint:
3715            kwargs["hint"] = hint
3716
3717        if self._match(TokenType.MATCH_CONDITION):
3718            kwargs["match_condition"] = self._parse_wrapped(self._parse_comparison)
3719
3720        if self._match(TokenType.ON):
3721            kwargs["on"] = self._parse_assignment()
3722        elif self._match(TokenType.USING):
3723            kwargs["using"] = self._parse_using_identifiers()
3724        elif (
3725            not (outer_apply or cross_apply)
3726            and not isinstance(kwargs["this"], exp.Unnest)
3727            and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
3728        ):
3729            index = self._index
3730            joins: t.Optional[list] = list(self._parse_joins())
3731
3732            if joins and self._match(TokenType.ON):
3733                kwargs["on"] = self._parse_assignment()
3734            elif joins and self._match(TokenType.USING):
3735                kwargs["using"] = self._parse_using_identifiers()
3736            else:
3737                joins = None
3738                self._retreat(index)
3739
3740            kwargs["this"].set("joins", joins if joins else None)
3741
3742        kwargs["pivots"] = self._parse_pivots()
3743
3744        comments = [c for token in (method, side, kind) if token for c in token.comments]
3745        return self.expression(exp.Join, comments=comments, **kwargs)
3746
3747    def _parse_opclass(self) -> t.Optional[exp.Expression]:
3748        this = self._parse_assignment()
3749
3750        if self._match_texts(self.OPCLASS_FOLLOW_KEYWORDS, advance=False):
3751            return this
3752
3753        if not self._match_set(self.OPTYPE_FOLLOW_TOKENS, advance=False):
3754            return self.expression(exp.Opclass, this=this, expression=self._parse_table_parts())
3755
3756        return this
3757
3758    def _parse_index_params(self) -> exp.IndexParameters:
3759        using = self._parse_var(any_token=True) if self._match(TokenType.USING) else None
3760
3761        if self._match(TokenType.L_PAREN, advance=False):
3762            columns = self._parse_wrapped_csv(self._parse_with_operator)
3763        else:
3764            columns = None
3765
3766        include = self._parse_wrapped_id_vars() if self._match_text_seq("INCLUDE") else None
3767        partition_by = self._parse_partition_by()
3768        with_storage = self._match(TokenType.WITH) and self._parse_wrapped_properties()
3769        tablespace = (
3770            self._parse_var(any_token=True)
3771            if self._match_text_seq("USING", "INDEX", "TABLESPACE")
3772            else None
3773        )
3774        where = self._parse_where()
3775
3776        on = self._parse_field() if self._match(TokenType.ON) else None
3777
3778        return self.expression(
3779            exp.IndexParameters,
3780            using=using,
3781            columns=columns,
3782            include=include,
3783            partition_by=partition_by,
3784            where=where,
3785            with_storage=with_storage,
3786            tablespace=tablespace,
3787            on=on,
3788        )
3789
3790    def _parse_index(
3791        self, index: t.Optional[exp.Expression] = None, anonymous: bool = False
3792    ) -> t.Optional[exp.Index]:
3793        if index or anonymous:
3794            unique = None
3795            primary = None
3796            amp = None
3797
3798            self._match(TokenType.ON)
3799            self._match(TokenType.TABLE)  # hive
3800            table = self._parse_table_parts(schema=True)
3801        else:
3802            unique = self._match(TokenType.UNIQUE)
3803            primary = self._match_text_seq("PRIMARY")
3804            amp = self._match_text_seq("AMP")
3805
3806            if not self._match(TokenType.INDEX):
3807                return None
3808
3809            index = self._parse_id_var()
3810            table = None
3811
3812        params = self._parse_index_params()
3813
3814        return self.expression(
3815            exp.Index,
3816            this=index,
3817            table=table,
3818            unique=unique,
3819            primary=primary,
3820            amp=amp,
3821            params=params,
3822        )
3823
3824    def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]:
3825        hints: t.List[exp.Expression] = []
3826        if self._match_pair(TokenType.WITH, TokenType.L_PAREN):
3827            # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16
3828            hints.append(
3829                self.expression(
3830                    exp.WithTableHint,
3831                    expressions=self._parse_csv(
3832                        lambda: self._parse_function() or self._parse_var(any_token=True)
3833                    ),
3834                )
3835            )
3836            self._match_r_paren()
3837        else:
3838            # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html
3839            while self._match_set(self.TABLE_INDEX_HINT_TOKENS):
3840                hint = exp.IndexTableHint(this=self._prev.text.upper())
3841
3842                self._match_set((TokenType.INDEX, TokenType.KEY))
3843                if self._match(TokenType.FOR):
3844                    hint.set("target", self._advance_any() and self._prev.text.upper())
3845
3846                hint.set("expressions", self._parse_wrapped_id_vars())
3847                hints.append(hint)
3848
3849        return hints or None
3850
3851    def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]:
3852        return (
3853            (not schema and self._parse_function(optional_parens=False))
3854            or self._parse_id_var(any_token=False)
3855            or self._parse_string_as_identifier()
3856            or self._parse_placeholder()
3857        )
3858
3859    def _parse_table_parts(
3860        self, schema: bool = False, is_db_reference: bool = False, wildcard: bool = False
3861    ) -> exp.Table:
3862        catalog = None
3863        db = None
3864        table: t.Optional[exp.Expression | str] = self._parse_table_part(schema=schema)
3865
3866        while self._match(TokenType.DOT):
3867            if catalog:
3868                # This allows nesting the table in arbitrarily many dot expressions if needed
3869                table = self.expression(
3870                    exp.Dot, this=table, expression=self._parse_table_part(schema=schema)
3871                )
3872            else:
3873                catalog = db
3874                db = table
3875                # "" used for tsql FROM a..b case
3876                table = self._parse_table_part(schema=schema) or ""
3877
3878        if (
3879            wildcard
3880            and self._is_connected()
3881            and (isinstance(table, exp.Identifier) or not table)
3882            and self._match(TokenType.STAR)
3883        ):
3884            if isinstance(table, exp.Identifier):
3885                table.args["this"] += "*"
3886            else:
3887                table = exp.Identifier(this="*")
3888
3889        # We bubble up comments from the Identifier to the Table
3890        comments = table.pop_comments() if isinstance(table, exp.Expression) else None
3891
3892        if is_db_reference:
3893            catalog = db
3894            db = table
3895            table = None
3896
3897        if not table and not is_db_reference:
3898            self.raise_error(f"Expected table name but got {self._curr}")
3899        if not db and is_db_reference:
3900            self.raise_error(f"Expected database name but got {self._curr}")
3901
3902        table = self.expression(
3903            exp.Table,
3904            comments=comments,
3905            this=table,
3906            db=db,
3907            catalog=catalog,
3908        )
3909
3910        changes = self._parse_changes()
3911        if changes:
3912            table.set("changes", changes)
3913
3914        at_before = self._parse_historical_data()
3915        if at_before:
3916            table.set("when", at_before)
3917
3918        pivots = self._parse_pivots()
3919        if pivots:
3920            table.set("pivots", pivots)
3921
3922        return table
3923
3924    def _parse_table(
3925        self,
3926        schema: bool = False,
3927        joins: bool = False,
3928        alias_tokens: t.Optional[t.Collection[TokenType]] = None,
3929        parse_bracket: bool = False,
3930        is_db_reference: bool = False,
3931        parse_partition: bool = False,
3932    ) -> t.Optional[exp.Expression]:
3933        lateral = self._parse_lateral()
3934        if lateral:
3935            return lateral
3936
3937        unnest = self._parse_unnest()
3938        if unnest:
3939            return unnest
3940
3941        values = self._parse_derived_table_values()
3942        if values:
3943            return values
3944
3945        subquery = self._parse_select(table=True)
3946        if subquery:
3947            if not subquery.args.get("pivots"):
3948                subquery.set("pivots", self._parse_pivots())
3949            return subquery
3950
3951        bracket = parse_bracket and self._parse_bracket(None)
3952        bracket = self.expression(exp.Table, this=bracket) if bracket else None
3953
3954        rows_from = self._match_text_seq("ROWS", "FROM") and self._parse_wrapped_csv(
3955            self._parse_table
3956        )
3957        rows_from = self.expression(exp.Table, rows_from=rows_from) if rows_from else None
3958
3959        only = self._match(TokenType.ONLY)
3960
3961        this = t.cast(
3962            exp.Expression,
3963            bracket
3964            or rows_from
3965            or self._parse_bracket(
3966                self._parse_table_parts(schema=schema, is_db_reference=is_db_reference)
3967            ),
3968        )
3969
3970        if only:
3971            this.set("only", only)
3972
3973        # Postgres supports a wildcard (table) suffix operator, which is a no-op in this context
3974        self._match_text_seq("*")
3975
3976        parse_partition = parse_partition or self.SUPPORTS_PARTITION_SELECTION
3977        if parse_partition and self._match(TokenType.PARTITION, advance=False):
3978            this.set("partition", self._parse_partition())
3979
3980        if schema:
3981            return self._parse_schema(this=this)
3982
3983        version = self._parse_version()
3984
3985        if version:
3986            this.set("version", version)
3987
3988        if self.dialect.ALIAS_POST_TABLESAMPLE:
3989            this.set("sample", self._parse_table_sample())
3990
3991        alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3992        if alias:
3993            this.set("alias", alias)
3994
3995        if isinstance(this, exp.Table) and self._match_text_seq("AT"):
3996            return self.expression(
3997                exp.AtIndex, this=this.to_column(copy=False), expression=self._parse_id_var()
3998            )
3999
4000        this.set("hints", self._parse_table_hints())
4001
4002        if not this.args.get("pivots"):
4003            this.set("pivots", self._parse_pivots())
4004
4005        if not self.dialect.ALIAS_POST_TABLESAMPLE:
4006            this.set("sample", self._parse_table_sample())
4007
4008        if joins:
4009            for join in self._parse_joins():
4010                this.append("joins", join)
4011
4012        if self._match_pair(TokenType.WITH, TokenType.ORDINALITY):
4013            this.set("ordinality", True)
4014            this.set("alias", self._parse_table_alias())
4015
4016        return this
4017
4018    def _parse_version(self) -> t.Optional[exp.Version]:
4019        if self._match(TokenType.TIMESTAMP_SNAPSHOT):
4020            this = "TIMESTAMP"
4021        elif self._match(TokenType.VERSION_SNAPSHOT):
4022            this = "VERSION"
4023        else:
4024            return None
4025
4026        if self._match_set((TokenType.FROM, TokenType.BETWEEN)):
4027            kind = self._prev.text.upper()
4028            start = self._parse_bitwise()
4029            self._match_texts(("TO", "AND"))
4030            end = self._parse_bitwise()
4031            expression: t.Optional[exp.Expression] = self.expression(
4032                exp.Tuple, expressions=[start, end]
4033            )
4034        elif self._match_text_seq("CONTAINED", "IN"):
4035            kind = "CONTAINED IN"
4036            expression = self.expression(
4037                exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise)
4038            )
4039        elif self._match(TokenType.ALL):
4040            kind = "ALL"
4041            expression = None
4042        else:
4043            self._match_text_seq("AS", "OF")
4044            kind = "AS OF"
4045            expression = self._parse_type()
4046
4047        return self.expression(exp.Version, this=this, expression=expression, kind=kind)
4048
4049    def _parse_historical_data(self) -> t.Optional[exp.HistoricalData]:
4050        # https://docs.snowflake.com/en/sql-reference/constructs/at-before
4051        index = self._index
4052        historical_data = None
4053        if self._match_texts(self.HISTORICAL_DATA_PREFIX):
4054            this = self._prev.text.upper()
4055            kind = (
4056                self._match(TokenType.L_PAREN)
4057                and self._match_texts(self.HISTORICAL_DATA_KIND)
4058                and self._prev.text.upper()
4059            )
4060            expression = self._match(TokenType.FARROW) and self._parse_bitwise()
4061
4062            if expression:
4063                self._match_r_paren()
4064                historical_data = self.expression(
4065                    exp.HistoricalData, this=this, kind=kind, expression=expression
4066                )
4067            else:
4068                self._retreat(index)
4069
4070        return historical_data
4071
4072    def _parse_changes(self) -> t.Optional[exp.Changes]:
4073        if not self._match_text_seq("CHANGES", "(", "INFORMATION", "=>"):
4074            return None
4075
4076        information = self._parse_var(any_token=True)
4077        self._match_r_paren()
4078
4079        return self.expression(
4080            exp.Changes,
4081            information=information,
4082            at_before=self._parse_historical_data(),
4083            end=self._parse_historical_data(),
4084        )
4085
4086    def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]:
4087        if not self._match(TokenType.UNNEST):
4088            return None
4089
4090        expressions = self._parse_wrapped_csv(self._parse_equality)
4091        offset = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
4092
4093        alias = self._parse_table_alias() if with_alias else None
4094
4095        if alias:
4096            if self.dialect.UNNEST_COLUMN_ONLY:
4097                if alias.args.get("columns"):
4098                    self.raise_error("Unexpected extra column alias in unnest.")
4099
4100                alias.set("columns", [alias.this])
4101                alias.set("this", None)
4102
4103            columns = alias.args.get("columns") or []
4104            if offset and len(expressions) < len(columns):
4105                offset = columns.pop()
4106
4107        if not offset and self._match_pair(TokenType.WITH, TokenType.OFFSET):
4108            self._match(TokenType.ALIAS)
4109            offset = self._parse_id_var(
4110                any_token=False, tokens=self.UNNEST_OFFSET_ALIAS_TOKENS
4111            ) or exp.to_identifier("offset")
4112
4113        return self.expression(exp.Unnest, expressions=expressions, alias=alias, offset=offset)
4114
4115    def _parse_derived_table_values(self) -> t.Optional[exp.Values]:
4116        is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES)
4117        if not is_derived and not (
4118            # ClickHouse's `FORMAT Values` is equivalent to `VALUES`
4119            self._match_text_seq("VALUES") or self._match_text_seq("FORMAT", "VALUES")
4120        ):
4121            return None
4122
4123        expressions = self._parse_csv(self._parse_value)
4124        alias = self._parse_table_alias()
4125
4126        if is_derived:
4127            self._match_r_paren()
4128
4129        return self.expression(
4130            exp.Values, expressions=expressions, alias=alias or self._parse_table_alias()
4131        )
4132
4133    def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]:
4134        if not self._match(TokenType.TABLE_SAMPLE) and not (
4135            as_modifier and self._match_text_seq("USING", "SAMPLE")
4136        ):
4137            return None
4138
4139        bucket_numerator = None
4140        bucket_denominator = None
4141        bucket_field = None
4142        percent = None
4143        size = None
4144        seed = None
4145
4146        method = self._parse_var(tokens=(TokenType.ROW,), upper=True)
4147        matched_l_paren = self._match(TokenType.L_PAREN)
4148
4149        if self.TABLESAMPLE_CSV:
4150            num = None
4151            expressions = self._parse_csv(self._parse_primary)
4152        else:
4153            expressions = None
4154            num = (
4155                self._parse_factor()
4156                if self._match(TokenType.NUMBER, advance=False)
4157                else self._parse_primary() or self._parse_placeholder()
4158            )
4159
4160        if self._match_text_seq("BUCKET"):
4161            bucket_numerator = self._parse_number()
4162            self._match_text_seq("OUT", "OF")
4163            bucket_denominator = bucket_denominator = self._parse_number()
4164            self._match(TokenType.ON)
4165            bucket_field = self._parse_field()
4166        elif self._match_set((TokenType.PERCENT, TokenType.MOD)):
4167            percent = num
4168        elif self._match(TokenType.ROWS) or not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
4169            size = num
4170        else:
4171            percent = num
4172
4173        if matched_l_paren:
4174            self._match_r_paren()
4175
4176        if self._match(TokenType.L_PAREN):
4177            method = self._parse_var(upper=True)
4178            seed = self._match(TokenType.COMMA) and self._parse_number()
4179            self._match_r_paren()
4180        elif self._match_texts(("SEED", "REPEATABLE")):
4181            seed = self._parse_wrapped(self._parse_number)
4182
4183        if not method and self.DEFAULT_SAMPLING_METHOD:
4184            method = exp.var(self.DEFAULT_SAMPLING_METHOD)
4185
4186        return self.expression(
4187            exp.TableSample,
4188            expressions=expressions,
4189            method=method,
4190            bucket_numerator=bucket_numerator,
4191            bucket_denominator=bucket_denominator,
4192            bucket_field=bucket_field,
4193            percent=percent,
4194            size=size,
4195            seed=seed,
4196        )
4197
4198    def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]:
4199        return list(iter(self._parse_pivot, None)) or None
4200
4201    def _parse_joins(self) -> t.Iterator[exp.Join]:
4202        return iter(self._parse_join, None)
4203
4204    def _parse_unpivot_columns(self) -> t.Optional[exp.UnpivotColumns]:
4205        if not self._match(TokenType.INTO):
4206            return None
4207
4208        return self.expression(
4209            exp.UnpivotColumns,
4210            this=self._match_text_seq("NAME") and self._parse_column(),
4211            expressions=self._match_text_seq("VALUE") and self._parse_csv(self._parse_column),
4212        )
4213
4214    # https://duckdb.org/docs/sql/statements/pivot
4215    def _parse_simplified_pivot(self, is_unpivot: t.Optional[bool] = None) -> exp.Pivot:
4216        def _parse_on() -> t.Optional[exp.Expression]:
4217            this = self._parse_bitwise()
4218
4219            if self._match(TokenType.IN):
4220                # PIVOT ... ON col IN (row_val1, row_val2)
4221                return self._parse_in(this)
4222            if self._match(TokenType.ALIAS, advance=False):
4223                # UNPIVOT ... ON (col1, col2, col3) AS row_val
4224                return self._parse_alias(this)
4225
4226            return this
4227
4228        this = self._parse_table()
4229        expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on)
4230        into = self._parse_unpivot_columns()
4231        using = self._match(TokenType.USING) and self._parse_csv(
4232            lambda: self._parse_alias(self._parse_function())
4233        )
4234        group = self._parse_group()
4235
4236        return self.expression(
4237            exp.Pivot,
4238            this=this,
4239            expressions=expressions,
4240            using=using,
4241            group=group,
4242            unpivot=is_unpivot,
4243            into=into,
4244        )
4245
4246    def _parse_pivot_in(self) -> exp.In:
4247        def _parse_aliased_expression() -> t.Optional[exp.Expression]:
4248            this = self._parse_select_or_expression()
4249
4250            self._match(TokenType.ALIAS)
4251            alias = self._parse_bitwise()
4252            if alias:
4253                if isinstance(alias, exp.Column) and not alias.db:
4254                    alias = alias.this
4255                return self.expression(exp.PivotAlias, this=this, alias=alias)
4256
4257            return this
4258
4259        value = self._parse_column()
4260
4261        if not self._match_pair(TokenType.IN, TokenType.L_PAREN):
4262            self.raise_error("Expecting IN (")
4263
4264        if self._match(TokenType.ANY):
4265            exprs: t.List[exp.Expression] = ensure_list(exp.PivotAny(this=self._parse_order()))
4266        else:
4267            exprs = self._parse_csv(_parse_aliased_expression)
4268
4269        self._match_r_paren()
4270        return self.expression(exp.In, this=value, expressions=exprs)
4271
4272    def _parse_pivot(self) -> t.Optional[exp.Pivot]:
4273        index = self._index
4274        include_nulls = None
4275
4276        if self._match(TokenType.PIVOT):
4277            unpivot = False
4278        elif self._match(TokenType.UNPIVOT):
4279            unpivot = True
4280
4281            # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax
4282            if self._match_text_seq("INCLUDE", "NULLS"):
4283                include_nulls = True
4284            elif self._match_text_seq("EXCLUDE", "NULLS"):
4285                include_nulls = False
4286        else:
4287            return None
4288
4289        expressions = []
4290
4291        if not self._match(TokenType.L_PAREN):
4292            self._retreat(index)
4293            return None
4294
4295        if unpivot:
4296            expressions = self._parse_csv(self._parse_column)
4297        else:
4298            expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function()))
4299
4300        if not expressions:
4301            self.raise_error("Failed to parse PIVOT's aggregation list")
4302
4303        if not self._match(TokenType.FOR):
4304            self.raise_error("Expecting FOR")
4305
4306        fields = []
4307        while True:
4308            field = self._try_parse(self._parse_pivot_in)
4309            if not field:
4310                break
4311            fields.append(field)
4312
4313        default_on_null = self._match_text_seq("DEFAULT", "ON", "NULL") and self._parse_wrapped(
4314            self._parse_bitwise
4315        )
4316
4317        group = self._parse_group()
4318
4319        self._match_r_paren()
4320
4321        pivot = self.expression(
4322            exp.Pivot,
4323            expressions=expressions,
4324            fields=fields,
4325            unpivot=unpivot,
4326            include_nulls=include_nulls,
4327            default_on_null=default_on_null,
4328            group=group,
4329        )
4330
4331        if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False):
4332            pivot.set("alias", self._parse_table_alias())
4333
4334        if not unpivot:
4335            names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions))
4336
4337            columns: t.List[exp.Expression] = []
4338            all_fields = []
4339            for pivot_field in pivot.fields:
4340                pivot_field_expressions = pivot_field.expressions
4341
4342                # The `PivotAny` expression corresponds to `ANY ORDER BY <column>`; we can't infer in this case.
4343                if isinstance(seq_get(pivot_field_expressions, 0), exp.PivotAny):
4344                    continue
4345
4346                all_fields.append(
4347                    [
4348                        fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name
4349                        for fld in pivot_field_expressions
4350                    ]
4351                )
4352
4353            if all_fields:
4354                if names:
4355                    all_fields.append(names)
4356
4357                # Generate all possible combinations of the pivot columns
4358                # e.g PIVOT(sum(...) as total FOR year IN (2000, 2010) FOR country IN ('NL', 'US'))
4359                # generates the product between [[2000, 2010], ['NL', 'US'], ['total']]
4360                for fld_parts_tuple in itertools.product(*all_fields):
4361                    fld_parts = list(fld_parts_tuple)
4362
4363                    if names and self.PREFIXED_PIVOT_COLUMNS:
4364                        # Move the "name" to the front of the list
4365                        fld_parts.insert(0, fld_parts.pop(-1))
4366
4367                    columns.append(exp.to_identifier("_".join(fld_parts)))
4368
4369            pivot.set("columns", columns)
4370
4371        return pivot
4372
4373    def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]:
4374        return [agg.alias for agg in aggregations if agg.alias]
4375
4376    def _parse_prewhere(self, skip_where_token: bool = False) -> t.Optional[exp.PreWhere]:
4377        if not skip_where_token and not self._match(TokenType.PREWHERE):
4378            return None
4379
4380        return self.expression(
4381            exp.PreWhere, comments=self._prev_comments, this=self._parse_assignment()
4382        )
4383
4384    def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]:
4385        if not skip_where_token and not self._match(TokenType.WHERE):
4386            return None
4387
4388        return self.expression(
4389            exp.Where, comments=self._prev_comments, this=self._parse_assignment()
4390        )
4391
4392    def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]:
4393        if not skip_group_by_token and not self._match(TokenType.GROUP_BY):
4394            return None
4395
4396        elements: t.Dict[str, t.Any] = defaultdict(list)
4397
4398        if self._match(TokenType.ALL):
4399            elements["all"] = True
4400        elif self._match(TokenType.DISTINCT):
4401            elements["all"] = False
4402
4403        while True:
4404            index = self._index
4405
4406            elements["expressions"].extend(
4407                self._parse_csv(
4408                    lambda: None
4409                    if self._match_set((TokenType.CUBE, TokenType.ROLLUP), advance=False)
4410                    else self._parse_assignment()
4411                )
4412            )
4413
4414            before_with_index = self._index
4415            with_prefix = self._match(TokenType.WITH)
4416
4417            if self._match(TokenType.ROLLUP):
4418                elements["rollup"].append(
4419                    self._parse_cube_or_rollup(exp.Rollup, with_prefix=with_prefix)
4420                )
4421            elif self._match(TokenType.CUBE):
4422                elements["cube"].append(
4423                    self._parse_cube_or_rollup(exp.Cube, with_prefix=with_prefix)
4424                )
4425            elif self._match(TokenType.GROUPING_SETS):
4426                elements["grouping_sets"].append(
4427                    self.expression(
4428                        exp.GroupingSets,
4429                        expressions=self._parse_wrapped_csv(self._parse_grouping_set),
4430                    )
4431                )
4432            elif self._match_text_seq("TOTALS"):
4433                elements["totals"] = True  # type: ignore
4434
4435            if before_with_index <= self._index <= before_with_index + 1:
4436                self._retreat(before_with_index)
4437                break
4438
4439            if index == self._index:
4440                break
4441
4442        return self.expression(exp.Group, **elements)  # type: ignore
4443
4444    def _parse_cube_or_rollup(self, kind: t.Type[E], with_prefix: bool = False) -> E:
4445        return self.expression(
4446            kind, expressions=[] if with_prefix else self._parse_wrapped_csv(self._parse_column)
4447        )
4448
4449    def _parse_grouping_set(self) -> t.Optional[exp.Expression]:
4450        if self._match(TokenType.L_PAREN):
4451            grouping_set = self._parse_csv(self._parse_column)
4452            self._match_r_paren()
4453            return self.expression(exp.Tuple, expressions=grouping_set)
4454
4455        return self._parse_column()
4456
4457    def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]:
4458        if not skip_having_token and not self._match(TokenType.HAVING):
4459            return None
4460        return self.expression(exp.Having, this=self._parse_assignment())
4461
4462    def _parse_qualify(self) -> t.Optional[exp.Qualify]:
4463        if not self._match(TokenType.QUALIFY):
4464            return None
4465        return self.expression(exp.Qualify, this=self._parse_assignment())
4466
4467    def _parse_connect_with_prior(self) -> t.Optional[exp.Expression]:
4468        self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression(
4469            exp.Prior, this=self._parse_bitwise()
4470        )
4471        connect = self._parse_assignment()
4472        self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR")
4473        return connect
4474
4475    def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]:
4476        if skip_start_token:
4477            start = None
4478        elif self._match(TokenType.START_WITH):
4479            start = self._parse_assignment()
4480        else:
4481            return None
4482
4483        self._match(TokenType.CONNECT_BY)
4484        nocycle = self._match_text_seq("NOCYCLE")
4485        connect = self._parse_connect_with_prior()
4486
4487        if not start and self._match(TokenType.START_WITH):
4488            start = self._parse_assignment()
4489
4490        return self.expression(exp.Connect, start=start, connect=connect, nocycle=nocycle)
4491
4492    def _parse_name_as_expression(self) -> t.Optional[exp.Expression]:
4493        this = self._parse_id_var(any_token=True)
4494        if self._match(TokenType.ALIAS):
4495            this = self.expression(exp.Alias, alias=this, this=self._parse_assignment())
4496        return this
4497
4498    def _parse_interpolate(self) -> t.Optional[t.List[exp.Expression]]:
4499        if self._match_text_seq("INTERPOLATE"):
4500            return self._parse_wrapped_csv(self._parse_name_as_expression)
4501        return None
4502
4503    def _parse_order(
4504        self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False
4505    ) -> t.Optional[exp.Expression]:
4506        siblings = None
4507        if not skip_order_token and not self._match(TokenType.ORDER_BY):
4508            if not self._match(TokenType.ORDER_SIBLINGS_BY):
4509                return this
4510
4511            siblings = True
4512
4513        return self.expression(
4514            exp.Order,
4515            this=this,
4516            expressions=self._parse_csv(self._parse_ordered),
4517            siblings=siblings,
4518        )
4519
4520    def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]:
4521        if not self._match(token):
4522            return None
4523        return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered))
4524
4525    def _parse_ordered(
4526        self, parse_method: t.Optional[t.Callable] = None
4527    ) -> t.Optional[exp.Ordered]:
4528        this = parse_method() if parse_method else self._parse_assignment()
4529        if not this:
4530            return None
4531
4532        if this.name.upper() == "ALL" and self.dialect.SUPPORTS_ORDER_BY_ALL:
4533            this = exp.var("ALL")
4534
4535        asc = self._match(TokenType.ASC)
4536        desc = self._match(TokenType.DESC) or (asc and False)
4537
4538        is_nulls_first = self._match_text_seq("NULLS", "FIRST")
4539        is_nulls_last = self._match_text_seq("NULLS", "LAST")
4540
4541        nulls_first = is_nulls_first or False
4542        explicitly_null_ordered = is_nulls_first or is_nulls_last
4543
4544        if (
4545            not explicitly_null_ordered
4546            and (
4547                (not desc and self.dialect.NULL_ORDERING == "nulls_are_small")
4548                or (desc and self.dialect.NULL_ORDERING != "nulls_are_small")
4549            )
4550            and self.dialect.NULL_ORDERING != "nulls_are_last"
4551        ):
4552            nulls_first = True
4553
4554        if self._match_text_seq("WITH", "FILL"):
4555            with_fill = self.expression(
4556                exp.WithFill,
4557                **{  # type: ignore
4558                    "from": self._match(TokenType.FROM) and self._parse_bitwise(),
4559                    "to": self._match_text_seq("TO") and self._parse_bitwise(),
4560                    "step": self._match_text_seq("STEP") and self._parse_bitwise(),
4561                    "interpolate": self._parse_interpolate(),
4562                },
4563            )
4564        else:
4565            with_fill = None
4566
4567        return self.expression(
4568            exp.Ordered, this=this, desc=desc, nulls_first=nulls_first, with_fill=with_fill
4569        )
4570
4571    def _parse_limit_options(self) -> exp.LimitOptions:
4572        percent = self._match(TokenType.PERCENT)
4573        rows = self._match_set((TokenType.ROW, TokenType.ROWS))
4574        self._match_text_seq("ONLY")
4575        with_ties = self._match_text_seq("WITH", "TIES")
4576        return self.expression(exp.LimitOptions, percent=percent, rows=rows, with_ties=with_ties)
4577
4578    def _parse_limit(
4579        self,
4580        this: t.Optional[exp.Expression] = None,
4581        top: bool = False,
4582        skip_limit_token: bool = False,
4583    ) -> t.Optional[exp.Expression]:
4584        if skip_limit_token or self._match(TokenType.TOP if top else TokenType.LIMIT):
4585            comments = self._prev_comments
4586            if top:
4587                limit_paren = self._match(TokenType.L_PAREN)
4588                expression = self._parse_term() if limit_paren else self._parse_number()
4589
4590                if limit_paren:
4591                    self._match_r_paren()
4592
4593                limit_options = self._parse_limit_options()
4594            else:
4595                limit_options = None
4596                expression = self._parse_term()
4597
4598            if self._match(TokenType.COMMA):
4599                offset = expression
4600                expression = self._parse_term()
4601            else:
4602                offset = None
4603
4604            limit_exp = self.expression(
4605                exp.Limit,
4606                this=this,
4607                expression=expression,
4608                offset=offset,
4609                comments=comments,
4610                limit_options=limit_options,
4611                expressions=self._parse_limit_by(),
4612            )
4613
4614            return limit_exp
4615
4616        if self._match(TokenType.FETCH):
4617            direction = self._match_set((TokenType.FIRST, TokenType.NEXT))
4618            direction = self._prev.text.upper() if direction else "FIRST"
4619
4620            count = self._parse_field(tokens=self.FETCH_TOKENS)
4621
4622            return self.expression(
4623                exp.Fetch,
4624                direction=direction,
4625                count=count,
4626                limit_options=self._parse_limit_options(),
4627            )
4628
4629        return this
4630
4631    def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4632        if not self._match(TokenType.OFFSET):
4633            return this
4634
4635        count = self._parse_term()
4636        self._match_set((TokenType.ROW, TokenType.ROWS))
4637
4638        return self.expression(
4639            exp.Offset, this=this, expression=count, expressions=self._parse_limit_by()
4640        )
4641
4642    def _can_parse_limit_or_offset(self) -> bool:
4643        if not self._match_set(self.AMBIGUOUS_ALIAS_TOKENS, advance=False):
4644            return False
4645
4646        index = self._index
4647        result = bool(
4648            self._try_parse(self._parse_limit, retreat=True)
4649            or self._try_parse(self._parse_offset, retreat=True)
4650        )
4651        self._retreat(index)
4652        return result
4653
4654    def _parse_limit_by(self) -> t.Optional[t.List[exp.Expression]]:
4655        return self._match_text_seq("BY") and self._parse_csv(self._parse_bitwise)
4656
4657    def _parse_locks(self) -> t.List[exp.Lock]:
4658        locks = []
4659        while True:
4660            if self._match_text_seq("FOR", "UPDATE"):
4661                update = True
4662            elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq(
4663                "LOCK", "IN", "SHARE", "MODE"
4664            ):
4665                update = False
4666            else:
4667                break
4668
4669            expressions = None
4670            if self._match_text_seq("OF"):
4671                expressions = self._parse_csv(lambda: self._parse_table(schema=True))
4672
4673            wait: t.Optional[bool | exp.Expression] = None
4674            if self._match_text_seq("NOWAIT"):
4675                wait = True
4676            elif self._match_text_seq("WAIT"):
4677                wait = self._parse_primary()
4678            elif self._match_text_seq("SKIP", "LOCKED"):
4679                wait = False
4680
4681            locks.append(
4682                self.expression(exp.Lock, update=update, expressions=expressions, wait=wait)
4683            )
4684
4685        return locks
4686
4687    def parse_set_operation(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4688        start = self._index
4689        _, side_token, kind_token = self._parse_join_parts()
4690
4691        side = side_token.text if side_token else None
4692        kind = kind_token.text if kind_token else None
4693
4694        if not self._match_set(self.SET_OPERATIONS):
4695            self._retreat(start)
4696            return None
4697
4698        token_type = self._prev.token_type
4699
4700        if token_type == TokenType.UNION:
4701            operation: t.Type[exp.SetOperation] = exp.Union
4702        elif token_type == TokenType.EXCEPT:
4703            operation = exp.Except
4704        else:
4705            operation = exp.Intersect
4706
4707        comments = self._prev.comments
4708
4709        if self._match(TokenType.DISTINCT):
4710            distinct: t.Optional[bool] = True
4711        elif self._match(TokenType.ALL):
4712            distinct = False
4713        else:
4714            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4715            if distinct is None:
4716                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4717
4718        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4719            "STRICT", "CORRESPONDING"
4720        )
4721        if self._match_text_seq("CORRESPONDING"):
4722            by_name = True
4723            if not side and not kind:
4724                kind = "INNER"
4725
4726        on_column_list = None
4727        if by_name and self._match_texts(("ON", "BY")):
4728            on_column_list = self._parse_wrapped_csv(self._parse_column)
4729
4730        expression = self._parse_select(nested=True, parse_set_operation=False)
4731
4732        return self.expression(
4733            operation,
4734            comments=comments,
4735            this=this,
4736            distinct=distinct,
4737            by_name=by_name,
4738            expression=expression,
4739            side=side,
4740            kind=kind,
4741            on=on_column_list,
4742        )
4743
4744    def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4745        while this:
4746            setop = self.parse_set_operation(this)
4747            if not setop:
4748                break
4749            this = setop
4750
4751        if isinstance(this, exp.SetOperation) and self.MODIFIERS_ATTACHED_TO_SET_OP:
4752            expression = this.expression
4753
4754            if expression:
4755                for arg in self.SET_OP_MODIFIERS:
4756                    expr = expression.args.get(arg)
4757                    if expr:
4758                        this.set(arg, expr.pop())
4759
4760        return this
4761
4762    def _parse_expression(self) -> t.Optional[exp.Expression]:
4763        return self._parse_alias(self._parse_assignment())
4764
4765    def _parse_assignment(self) -> t.Optional[exp.Expression]:
4766        this = self._parse_disjunction()
4767        if not this and self._next and self._next.token_type in self.ASSIGNMENT:
4768            # This allows us to parse <non-identifier token> := <expr>
4769            this = exp.column(
4770                t.cast(str, self._advance_any(ignore_reserved=True) and self._prev.text)
4771            )
4772
4773        while self._match_set(self.ASSIGNMENT):
4774            if isinstance(this, exp.Column) and len(this.parts) == 1:
4775                this = this.this
4776
4777            this = self.expression(
4778                self.ASSIGNMENT[self._prev.token_type],
4779                this=this,
4780                comments=self._prev_comments,
4781                expression=self._parse_assignment(),
4782            )
4783
4784        return this
4785
4786    def _parse_disjunction(self) -> t.Optional[exp.Expression]:
4787        return self._parse_tokens(self._parse_conjunction, self.DISJUNCTION)
4788
4789    def _parse_conjunction(self) -> t.Optional[exp.Expression]:
4790        return self._parse_tokens(self._parse_equality, self.CONJUNCTION)
4791
4792    def _parse_equality(self) -> t.Optional[exp.Expression]:
4793        return self._parse_tokens(self._parse_comparison, self.EQUALITY)
4794
4795    def _parse_comparison(self) -> t.Optional[exp.Expression]:
4796        return self._parse_tokens(self._parse_range, self.COMPARISON)
4797
4798    def _parse_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4799        this = this or self._parse_bitwise()
4800        negate = self._match(TokenType.NOT)
4801
4802        if self._match_set(self.RANGE_PARSERS):
4803            expression = self.RANGE_PARSERS[self._prev.token_type](self, this)
4804            if not expression:
4805                return this
4806
4807            this = expression
4808        elif self._match(TokenType.ISNULL):
4809            this = self.expression(exp.Is, this=this, expression=exp.Null())
4810
4811        # Postgres supports ISNULL and NOTNULL for conditions.
4812        # https://blog.andreiavram.ro/postgresql-null-composite-type/
4813        if self._match(TokenType.NOTNULL):
4814            this = self.expression(exp.Is, this=this, expression=exp.Null())
4815            this = self.expression(exp.Not, this=this)
4816
4817        if negate:
4818            this = self._negate_range(this)
4819
4820        if self._match(TokenType.IS):
4821            this = self._parse_is(this)
4822
4823        return this
4824
4825    def _negate_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4826        if not this:
4827            return this
4828
4829        return self.expression(exp.Not, this=this)
4830
4831    def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4832        index = self._index - 1
4833        negate = self._match(TokenType.NOT)
4834
4835        if self._match_text_seq("DISTINCT", "FROM"):
4836            klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ
4837            return self.expression(klass, this=this, expression=self._parse_bitwise())
4838
4839        if self._match(TokenType.JSON):
4840            kind = self._match_texts(self.IS_JSON_PREDICATE_KIND) and self._prev.text.upper()
4841
4842            if self._match_text_seq("WITH"):
4843                _with = True
4844            elif self._match_text_seq("WITHOUT"):
4845                _with = False
4846            else:
4847                _with = None
4848
4849            unique = self._match(TokenType.UNIQUE)
4850            self._match_text_seq("KEYS")
4851            expression: t.Optional[exp.Expression] = self.expression(
4852                exp.JSON, **{"this": kind, "with": _with, "unique": unique}
4853            )
4854        else:
4855            expression = self._parse_primary() or self._parse_null()
4856            if not expression:
4857                self._retreat(index)
4858                return None
4859
4860        this = self.expression(exp.Is, this=this, expression=expression)
4861        return self.expression(exp.Not, this=this) if negate else this
4862
4863    def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In:
4864        unnest = self._parse_unnest(with_alias=False)
4865        if unnest:
4866            this = self.expression(exp.In, this=this, unnest=unnest)
4867        elif self._match_set((TokenType.L_PAREN, TokenType.L_BRACKET)):
4868            matched_l_paren = self._prev.token_type == TokenType.L_PAREN
4869            expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias))
4870
4871            if len(expressions) == 1 and isinstance(expressions[0], exp.Query):
4872                this = self.expression(exp.In, this=this, query=expressions[0].subquery(copy=False))
4873            else:
4874                this = self.expression(exp.In, this=this, expressions=expressions)
4875
4876            if matched_l_paren:
4877                self._match_r_paren(this)
4878            elif not self._match(TokenType.R_BRACKET, expression=this):
4879                self.raise_error("Expecting ]")
4880        else:
4881            this = self.expression(exp.In, this=this, field=self._parse_column())
4882
4883        return this
4884
4885    def _parse_between(self, this: t.Optional[exp.Expression]) -> exp.Between:
4886        low = self._parse_bitwise()
4887        self._match(TokenType.AND)
4888        high = self._parse_bitwise()
4889        return self.expression(exp.Between, this=this, low=low, high=high)
4890
4891    def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4892        if not self._match(TokenType.ESCAPE):
4893            return this
4894        return self.expression(exp.Escape, this=this, expression=self._parse_string())
4895
4896    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Add | exp.Interval]:
4897        index = self._index
4898
4899        if not self._match(TokenType.INTERVAL) and match_interval:
4900            return None
4901
4902        if self._match(TokenType.STRING, advance=False):
4903            this = self._parse_primary()
4904        else:
4905            this = self._parse_term()
4906
4907        if not this or (
4908            isinstance(this, exp.Column)
4909            and not this.table
4910            and not this.this.quoted
4911            and this.name.upper() == "IS"
4912        ):
4913            self._retreat(index)
4914            return None
4915
4916        unit = self._parse_function() or (
4917            not self._match(TokenType.ALIAS, advance=False)
4918            and self._parse_var(any_token=True, upper=True)
4919        )
4920
4921        # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse
4922        # each INTERVAL expression into this canonical form so it's easy to transpile
4923        if this and this.is_number:
4924            this = exp.Literal.string(this.to_py())
4925        elif this and this.is_string:
4926            parts = exp.INTERVAL_STRING_RE.findall(this.name)
4927            if parts and unit:
4928                # Unconsume the eagerly-parsed unit, since the real unit was part of the string
4929                unit = None
4930                self._retreat(self._index - 1)
4931
4932            if len(parts) == 1:
4933                this = exp.Literal.string(parts[0][0])
4934                unit = self.expression(exp.Var, this=parts[0][1].upper())
4935        if self.INTERVAL_SPANS and self._match_text_seq("TO"):
4936            unit = self.expression(
4937                exp.IntervalSpan, this=unit, expression=self._parse_var(any_token=True, upper=True)
4938            )
4939
4940        interval = self.expression(exp.Interval, this=this, unit=unit)
4941
4942        index = self._index
4943        self._match(TokenType.PLUS)
4944
4945        # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
4946        if self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
4947            return self.expression(
4948                exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
4949            )
4950
4951        self._retreat(index)
4952        return interval
4953
4954    def _parse_bitwise(self) -> t.Optional[exp.Expression]:
4955        this = self._parse_term()
4956
4957        while True:
4958            if self._match_set(self.BITWISE):
4959                this = self.expression(
4960                    self.BITWISE[self._prev.token_type],
4961                    this=this,
4962                    expression=self._parse_term(),
4963                )
4964            elif self.dialect.DPIPE_IS_STRING_CONCAT and self._match(TokenType.DPIPE):
4965                this = self.expression(
4966                    exp.DPipe,
4967                    this=this,
4968                    expression=self._parse_term(),
4969                    safe=not self.dialect.STRICT_STRING_CONCAT,
4970                )
4971            elif self._match(TokenType.DQMARK):
4972                this = self.expression(
4973                    exp.Coalesce, this=this, expressions=ensure_list(self._parse_term())
4974                )
4975            elif self._match_pair(TokenType.LT, TokenType.LT):
4976                this = self.expression(
4977                    exp.BitwiseLeftShift, this=this, expression=self._parse_term()
4978                )
4979            elif self._match_pair(TokenType.GT, TokenType.GT):
4980                this = self.expression(
4981                    exp.BitwiseRightShift, this=this, expression=self._parse_term()
4982                )
4983            else:
4984                break
4985
4986        return this
4987
4988    def _parse_term(self) -> t.Optional[exp.Expression]:
4989        this = self._parse_factor()
4990
4991        while self._match_set(self.TERM):
4992            klass = self.TERM[self._prev.token_type]
4993            comments = self._prev_comments
4994            expression = self._parse_factor()
4995
4996            this = self.expression(klass, this=this, comments=comments, expression=expression)
4997
4998            if isinstance(this, exp.Collate):
4999                expr = this.expression
5000
5001                # Preserve collations such as pg_catalog."default" (Postgres) as columns, otherwise
5002                # fallback to Identifier / Var
5003                if isinstance(expr, exp.Column) and len(expr.parts) == 1:
5004                    ident = expr.this
5005                    if isinstance(ident, exp.Identifier):
5006                        this.set("expression", ident if ident.quoted else exp.var(ident.name))
5007
5008        return this
5009
5010    def _parse_factor(self) -> t.Optional[exp.Expression]:
5011        parse_method = self._parse_exponent if self.EXPONENT else self._parse_unary
5012        this = parse_method()
5013
5014        while self._match_set(self.FACTOR):
5015            klass = self.FACTOR[self._prev.token_type]
5016            comments = self._prev_comments
5017            expression = parse_method()
5018
5019            if not expression and klass is exp.IntDiv and self._prev.text.isalpha():
5020                self._retreat(self._index - 1)
5021                return this
5022
5023            this = self.expression(klass, this=this, comments=comments, expression=expression)
5024
5025            if isinstance(this, exp.Div):
5026                this.args["typed"] = self.dialect.TYPED_DIVISION
5027                this.args["safe"] = self.dialect.SAFE_DIVISION
5028
5029        return this
5030
5031    def _parse_exponent(self) -> t.Optional[exp.Expression]:
5032        return self._parse_tokens(self._parse_unary, self.EXPONENT)
5033
5034    def _parse_unary(self) -> t.Optional[exp.Expression]:
5035        if self._match_set(self.UNARY_PARSERS):
5036            return self.UNARY_PARSERS[self._prev.token_type](self)
5037        return self._parse_at_time_zone(self._parse_type())
5038
5039    def _parse_type(
5040        self, parse_interval: bool = True, fallback_to_identifier: bool = False
5041    ) -> t.Optional[exp.Expression]:
5042        interval = parse_interval and self._parse_interval()
5043        if interval:
5044            return interval
5045
5046        index = self._index
5047        data_type = self._parse_types(check_func=True, allow_identifiers=False)
5048
5049        # parse_types() returns a Cast if we parsed BQ's inline constructor <type>(<values>) e.g.
5050        # STRUCT<a INT, b STRING>(1, 'foo'), which is canonicalized to CAST(<values> AS <type>)
5051        if isinstance(data_type, exp.Cast):
5052            # This constructor can contain ops directly after it, for instance struct unnesting:
5053            # STRUCT<a INT, b STRING>(1, 'foo').* --> CAST(STRUCT(1, 'foo') AS STRUCT<a iNT, b STRING).*
5054            return self._parse_column_ops(data_type)
5055
5056        if data_type:
5057            index2 = self._index
5058            this = self._parse_primary()
5059
5060            if isinstance(this, exp.Literal):
5061                this = self._parse_column_ops(this)
5062
5063                parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
5064                if parser:
5065                    return parser(self, this, data_type)
5066
5067                return self.expression(exp.Cast, this=this, to=data_type)
5068
5069            # The expressions arg gets set by the parser when we have something like DECIMAL(38, 0)
5070            # in the input SQL. In that case, we'll produce these tokens: DECIMAL ( 38 , 0 )
5071            #
5072            # If the index difference here is greater than 1, that means the parser itself must have
5073            # consumed additional tokens such as the DECIMAL scale and precision in the above example.
5074            #
5075            # If it's not greater than 1, then it must be 1, because we've consumed at least the type
5076            # keyword, meaning that the expressions arg of the DataType must have gotten set by a
5077            # callable in the TYPE_CONVERTERS mapping. For example, Snowflake converts DECIMAL to
5078            # DECIMAL(38, 0)) in order to facilitate the data type's transpilation.
5079            #
5080            # In these cases, we don't really want to return the converted type, but instead retreat
5081            # and try to parse a Column or Identifier in the section below.
5082            if data_type.expressions and index2 - index > 1:
5083                self._retreat(index2)
5084                return self._parse_column_ops(data_type)
5085
5086            self._retreat(index)
5087
5088        if fallback_to_identifier:
5089            return self._parse_id_var()
5090
5091        this = self._parse_column()
5092        return this and self._parse_column_ops(this)
5093
5094    def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]:
5095        this = self._parse_type()
5096        if not this:
5097            return None
5098
5099        if isinstance(this, exp.Column) and not this.table:
5100            this = exp.var(this.name.upper())
5101
5102        return self.expression(
5103            exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True)
5104        )
5105
5106    def _parse_types(
5107        self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
5108    ) -> t.Optional[exp.Expression]:
5109        index = self._index
5110
5111        this: t.Optional[exp.Expression] = None
5112        prefix = self._match_text_seq("SYSUDTLIB", ".")
5113
5114        if not self._match_set(self.TYPE_TOKENS):
5115            identifier = allow_identifiers and self._parse_id_var(
5116                any_token=False, tokens=(TokenType.VAR,)
5117            )
5118            if isinstance(identifier, exp.Identifier):
5119                tokens = self.dialect.tokenize(identifier.sql(dialect=self.dialect))
5120
5121                if len(tokens) != 1:
5122                    self.raise_error("Unexpected identifier", self._prev)
5123
5124                if tokens[0].token_type in self.TYPE_TOKENS:
5125                    self._prev = tokens[0]
5126                elif self.dialect.SUPPORTS_USER_DEFINED_TYPES:
5127                    type_name = identifier.name
5128
5129                    while self._match(TokenType.DOT):
5130                        type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
5131
5132                    this = exp.DataType.build(type_name, udt=True)
5133                else:
5134                    self._retreat(self._index - 1)
5135                    return None
5136            else:
5137                return None
5138
5139        type_token = self._prev.token_type
5140
5141        if type_token == TokenType.PSEUDO_TYPE:
5142            return self.expression(exp.PseudoType, this=self._prev.text.upper())
5143
5144        if type_token == TokenType.OBJECT_IDENTIFIER:
5145            return self.expression(exp.ObjectIdentifier, this=self._prev.text.upper())
5146
5147        # https://materialize.com/docs/sql/types/map/
5148        if type_token == TokenType.MAP and self._match(TokenType.L_BRACKET):
5149            key_type = self._parse_types(
5150                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5151            )
5152            if not self._match(TokenType.FARROW):
5153                self._retreat(index)
5154                return None
5155
5156            value_type = self._parse_types(
5157                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5158            )
5159            if not self._match(TokenType.R_BRACKET):
5160                self._retreat(index)
5161                return None
5162
5163            return exp.DataType(
5164                this=exp.DataType.Type.MAP,
5165                expressions=[key_type, value_type],
5166                nested=True,
5167                prefix=prefix,
5168            )
5169
5170        nested = type_token in self.NESTED_TYPE_TOKENS
5171        is_struct = type_token in self.STRUCT_TYPE_TOKENS
5172        is_aggregate = type_token in self.AGGREGATE_TYPE_TOKENS
5173        expressions = None
5174        maybe_func = False
5175
5176        if self._match(TokenType.L_PAREN):
5177            if is_struct:
5178                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5179            elif nested:
5180                expressions = self._parse_csv(
5181                    lambda: self._parse_types(
5182                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5183                    )
5184                )
5185                if type_token == TokenType.NULLABLE and len(expressions) == 1:
5186                    this = expressions[0]
5187                    this.set("nullable", True)
5188                    self._match_r_paren()
5189                    return this
5190            elif type_token in self.ENUM_TYPE_TOKENS:
5191                expressions = self._parse_csv(self._parse_equality)
5192            elif is_aggregate:
5193                func_or_ident = self._parse_function(anonymous=True) or self._parse_id_var(
5194                    any_token=False, tokens=(TokenType.VAR, TokenType.ANY)
5195                )
5196                if not func_or_ident:
5197                    return None
5198                expressions = [func_or_ident]
5199                if self._match(TokenType.COMMA):
5200                    expressions.extend(
5201                        self._parse_csv(
5202                            lambda: self._parse_types(
5203                                check_func=check_func,
5204                                schema=schema,
5205                                allow_identifiers=allow_identifiers,
5206                            )
5207                        )
5208                    )
5209            else:
5210                expressions = self._parse_csv(self._parse_type_size)
5211
5212                # https://docs.snowflake.com/en/sql-reference/data-types-vector
5213                if type_token == TokenType.VECTOR and len(expressions) == 2:
5214                    expressions[0] = exp.DataType.build(expressions[0].name, dialect=self.dialect)
5215
5216            if not expressions or not self._match(TokenType.R_PAREN):
5217                self._retreat(index)
5218                return None
5219
5220            maybe_func = True
5221
5222        values: t.Optional[t.List[exp.Expression]] = None
5223
5224        if nested and self._match(TokenType.LT):
5225            if is_struct:
5226                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5227            else:
5228                expressions = self._parse_csv(
5229                    lambda: self._parse_types(
5230                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5231                    )
5232                )
5233
5234            if not self._match(TokenType.GT):
5235                self.raise_error("Expecting >")
5236
5237            if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)):
5238                values = self._parse_csv(self._parse_assignment)
5239                if not values and is_struct:
5240                    values = None
5241                    self._retreat(self._index - 1)
5242                else:
5243                    self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN))
5244
5245        if type_token in self.TIMESTAMPS:
5246            if self._match_text_seq("WITH", "TIME", "ZONE"):
5247                maybe_func = False
5248                tz_type = (
5249                    exp.DataType.Type.TIMETZ
5250                    if type_token in self.TIMES
5251                    else exp.DataType.Type.TIMESTAMPTZ
5252                )
5253                this = exp.DataType(this=tz_type, expressions=expressions)
5254            elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"):
5255                maybe_func = False
5256                this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions)
5257            elif self._match_text_seq("WITHOUT", "TIME", "ZONE"):
5258                maybe_func = False
5259        elif type_token == TokenType.INTERVAL:
5260            unit = self._parse_var(upper=True)
5261            if unit:
5262                if self._match_text_seq("TO"):
5263                    unit = exp.IntervalSpan(this=unit, expression=self._parse_var(upper=True))
5264
5265                this = self.expression(exp.DataType, this=self.expression(exp.Interval, unit=unit))
5266            else:
5267                this = self.expression(exp.DataType, this=exp.DataType.Type.INTERVAL)
5268        elif type_token == TokenType.VOID:
5269            this = exp.DataType(this=exp.DataType.Type.NULL)
5270
5271        if maybe_func and check_func:
5272            index2 = self._index
5273            peek = self._parse_string()
5274
5275            if not peek:
5276                self._retreat(index)
5277                return None
5278
5279            self._retreat(index2)
5280
5281        if not this:
5282            if self._match_text_seq("UNSIGNED"):
5283                unsigned_type_token = self.SIGNED_TO_UNSIGNED_TYPE_TOKEN.get(type_token)
5284                if not unsigned_type_token:
5285                    self.raise_error(f"Cannot convert {type_token.value} to unsigned.")
5286
5287                type_token = unsigned_type_token or type_token
5288
5289            this = exp.DataType(
5290                this=exp.DataType.Type[type_token.value],
5291                expressions=expressions,
5292                nested=nested,
5293                prefix=prefix,
5294            )
5295
5296            # Empty arrays/structs are allowed
5297            if values is not None:
5298                cls = exp.Struct if is_struct else exp.Array
5299                this = exp.cast(cls(expressions=values), this, copy=False)
5300
5301        elif expressions:
5302            this.set("expressions", expressions)
5303
5304        # https://materialize.com/docs/sql/types/list/#type-name
5305        while self._match(TokenType.LIST):
5306            this = exp.DataType(this=exp.DataType.Type.LIST, expressions=[this], nested=True)
5307
5308        index = self._index
5309
5310        # Postgres supports the INT ARRAY[3] syntax as a synonym for INT[3]
5311        matched_array = self._match(TokenType.ARRAY)
5312
5313        while self._curr:
5314            datatype_token = self._prev.token_type
5315            matched_l_bracket = self._match(TokenType.L_BRACKET)
5316
5317            if (not matched_l_bracket and not matched_array) or (
5318                datatype_token == TokenType.ARRAY and self._match(TokenType.R_BRACKET)
5319            ):
5320                # Postgres allows casting empty arrays such as ARRAY[]::INT[],
5321                # not to be confused with the fixed size array parsing
5322                break
5323
5324            matched_array = False
5325            values = self._parse_csv(self._parse_assignment) or None
5326            if (
5327                values
5328                and not schema
5329                and (
5330                    not self.dialect.SUPPORTS_FIXED_SIZE_ARRAYS or datatype_token == TokenType.ARRAY
5331                )
5332            ):
5333                # Retreating here means that we should not parse the following values as part of the data type, e.g. in DuckDB
5334                # ARRAY[1] should retreat and instead be parsed into exp.Array in contrast to INT[x][y] which denotes a fixed-size array data type
5335                self._retreat(index)
5336                break
5337
5338            this = exp.DataType(
5339                this=exp.DataType.Type.ARRAY, expressions=[this], values=values, nested=True
5340            )
5341            self._match(TokenType.R_BRACKET)
5342
5343        if self.TYPE_CONVERTERS and isinstance(this.this, exp.DataType.Type):
5344            converter = self.TYPE_CONVERTERS.get(this.this)
5345            if converter:
5346                this = converter(t.cast(exp.DataType, this))
5347
5348        return this
5349
5350    def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]:
5351        index = self._index
5352
5353        if (
5354            self._curr
5355            and self._next
5356            and self._curr.token_type in self.TYPE_TOKENS
5357            and self._next.token_type in self.TYPE_TOKENS
5358        ):
5359            # Takes care of special cases like `STRUCT<list ARRAY<...>>` where the identifier is also a
5360            # type token. Without this, the list will be parsed as a type and we'll eventually crash
5361            this = self._parse_id_var()
5362        else:
5363            this = (
5364                self._parse_type(parse_interval=False, fallback_to_identifier=True)
5365                or self._parse_id_var()
5366            )
5367
5368        self._match(TokenType.COLON)
5369
5370        if (
5371            type_required
5372            and not isinstance(this, exp.DataType)
5373            and not self._match_set(self.TYPE_TOKENS, advance=False)
5374        ):
5375            self._retreat(index)
5376            return self._parse_types()
5377
5378        return self._parse_column_def(this)
5379
5380    def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5381        if not self._match_text_seq("AT", "TIME", "ZONE"):
5382            return this
5383        return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary())
5384
5385    def _parse_column(self) -> t.Optional[exp.Expression]:
5386        this = self._parse_column_reference()
5387        column = self._parse_column_ops(this) if this else self._parse_bracket(this)
5388
5389        if self.dialect.SUPPORTS_COLUMN_JOIN_MARKS and column:
5390            column.set("join_mark", self._match(TokenType.JOIN_MARKER))
5391
5392        return column
5393
5394    def _parse_column_reference(self) -> t.Optional[exp.Expression]:
5395        this = self._parse_field()
5396        if (
5397            not this
5398            and self._match(TokenType.VALUES, advance=False)
5399            and self.VALUES_FOLLOWED_BY_PAREN
5400            and (not self._next or self._next.token_type != TokenType.L_PAREN)
5401        ):
5402            this = self._parse_id_var()
5403
5404        if isinstance(this, exp.Identifier):
5405            # We bubble up comments from the Identifier to the Column
5406            this = self.expression(exp.Column, comments=this.pop_comments(), this=this)
5407
5408        return this
5409
5410    def _parse_colon_as_variant_extract(
5411        self, this: t.Optional[exp.Expression]
5412    ) -> t.Optional[exp.Expression]:
5413        casts = []
5414        json_path = []
5415        escape = None
5416
5417        while self._match(TokenType.COLON):
5418            start_index = self._index
5419
5420            # Snowflake allows reserved keywords as json keys but advance_any() excludes TokenType.SELECT from any_tokens=True
5421            path = self._parse_column_ops(
5422                self._parse_field(any_token=True, tokens=(TokenType.SELECT,))
5423            )
5424
5425            # The cast :: operator has a lower precedence than the extraction operator :, so
5426            # we rearrange the AST appropriately to avoid casting the JSON path
5427            while isinstance(path, exp.Cast):
5428                casts.append(path.to)
5429                path = path.this
5430
5431            if casts:
5432                dcolon_offset = next(
5433                    i
5434                    for i, t in enumerate(self._tokens[start_index:])
5435                    if t.token_type == TokenType.DCOLON
5436                )
5437                end_token = self._tokens[start_index + dcolon_offset - 1]
5438            else:
5439                end_token = self._prev
5440
5441            if path:
5442                # Escape single quotes from Snowflake's colon extraction (e.g. col:"a'b") as
5443                # it'll roundtrip to a string literal in GET_PATH
5444                if isinstance(path, exp.Identifier) and path.quoted:
5445                    escape = True
5446
5447                json_path.append(self._find_sql(self._tokens[start_index], end_token))
5448
5449        # The VARIANT extract in Snowflake/Databricks is parsed as a JSONExtract; Snowflake uses the json_path in GET_PATH() while
5450        # Databricks transforms it back to the colon/dot notation
5451        if json_path:
5452            json_path_expr = self.dialect.to_json_path(exp.Literal.string(".".join(json_path)))
5453
5454            if json_path_expr:
5455                json_path_expr.set("escape", escape)
5456
5457            this = self.expression(
5458                exp.JSONExtract,
5459                this=this,
5460                expression=json_path_expr,
5461                variant_extract=True,
5462            )
5463
5464            while casts:
5465                this = self.expression(exp.Cast, this=this, to=casts.pop())
5466
5467        return this
5468
5469    def _parse_dcolon(self) -> t.Optional[exp.Expression]:
5470        return self._parse_types()
5471
5472    def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5473        this = self._parse_bracket(this)
5474
5475        while self._match_set(self.COLUMN_OPERATORS):
5476            op_token = self._prev.token_type
5477            op = self.COLUMN_OPERATORS.get(op_token)
5478
5479            if op_token in (TokenType.DCOLON, TokenType.DOTCOLON):
5480                field = self._parse_dcolon()
5481                if not field:
5482                    self.raise_error("Expected type")
5483            elif op and self._curr:
5484                field = self._parse_column_reference() or self._parse_bracket()
5485                if isinstance(field, exp.Column) and self._match(TokenType.DOT, advance=False):
5486                    field = self._parse_column_ops(field)
5487            else:
5488                field = self._parse_field(any_token=True, anonymous_func=True)
5489
5490            if isinstance(field, (exp.Func, exp.Window)) and this:
5491                # BQ & snowflake allow function calls like x.y.count(...), SAFE.SUBSTR(...) etc
5492                # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules
5493                this = exp.replace_tree(
5494                    this,
5495                    lambda n: (
5496                        self.expression(exp.Dot, this=n.args.get("table"), expression=n.this)
5497                        if n.table
5498                        else n.this
5499                    )
5500                    if isinstance(n, exp.Column)
5501                    else n,
5502                )
5503
5504            if op:
5505                this = op(self, this, field)
5506            elif isinstance(this, exp.Column) and not this.args.get("catalog"):
5507                this = self.expression(
5508                    exp.Column,
5509                    comments=this.comments,
5510                    this=field,
5511                    table=this.this,
5512                    db=this.args.get("table"),
5513                    catalog=this.args.get("db"),
5514                )
5515            elif isinstance(field, exp.Window):
5516                # Move the exp.Dot's to the window's function
5517                window_func = self.expression(exp.Dot, this=this, expression=field.this)
5518                field.set("this", window_func)
5519                this = field
5520            else:
5521                this = self.expression(exp.Dot, this=this, expression=field)
5522
5523            if field and field.comments:
5524                t.cast(exp.Expression, this).add_comments(field.pop_comments())
5525
5526            this = self._parse_bracket(this)
5527
5528        return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
5529
5530    def _parse_primary(self) -> t.Optional[exp.Expression]:
5531        if self._match_set(self.PRIMARY_PARSERS):
5532            token_type = self._prev.token_type
5533            primary = self.PRIMARY_PARSERS[token_type](self, self._prev)
5534
5535            if token_type == TokenType.STRING:
5536                expressions = [primary]
5537                while self._match(TokenType.STRING):
5538                    expressions.append(exp.Literal.string(self._prev.text))
5539
5540                if len(expressions) > 1:
5541                    return self.expression(exp.Concat, expressions=expressions)
5542
5543            return primary
5544
5545        if self._match_pair(TokenType.DOT, TokenType.NUMBER):
5546            return exp.Literal.number(f"0.{self._prev.text}")
5547
5548        if self._match(TokenType.L_PAREN):
5549            comments = self._prev_comments
5550            query = self._parse_select()
5551
5552            if query:
5553                expressions = [query]
5554            else:
5555                expressions = self._parse_expressions()
5556
5557            this = self._parse_query_modifiers(seq_get(expressions, 0))
5558
5559            if not this and self._match(TokenType.R_PAREN, advance=False):
5560                this = self.expression(exp.Tuple)
5561            elif isinstance(this, exp.UNWRAPPED_QUERIES):
5562                this = self._parse_subquery(this=this, parse_alias=False)
5563            elif isinstance(this, exp.Subquery):
5564                this = self._parse_subquery(
5565                    this=self._parse_set_operations(this), parse_alias=False
5566                )
5567            elif len(expressions) > 1 or self._prev.token_type == TokenType.COMMA:
5568                this = self.expression(exp.Tuple, expressions=expressions)
5569            else:
5570                this = self.expression(exp.Paren, this=this)
5571
5572            if this:
5573                this.add_comments(comments)
5574
5575            self._match_r_paren(expression=this)
5576            return this
5577
5578        return None
5579
5580    def _parse_field(
5581        self,
5582        any_token: bool = False,
5583        tokens: t.Optional[t.Collection[TokenType]] = None,
5584        anonymous_func: bool = False,
5585    ) -> t.Optional[exp.Expression]:
5586        if anonymous_func:
5587            field = (
5588                self._parse_function(anonymous=anonymous_func, any_token=any_token)
5589                or self._parse_primary()
5590            )
5591        else:
5592            field = self._parse_primary() or self._parse_function(
5593                anonymous=anonymous_func, any_token=any_token
5594            )
5595        return field or self._parse_id_var(any_token=any_token, tokens=tokens)
5596
5597    def _parse_function(
5598        self,
5599        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5600        anonymous: bool = False,
5601        optional_parens: bool = True,
5602        any_token: bool = False,
5603    ) -> t.Optional[exp.Expression]:
5604        # This allows us to also parse {fn <function>} syntax (Snowflake, MySQL support this)
5605        # See: https://community.snowflake.com/s/article/SQL-Escape-Sequences
5606        fn_syntax = False
5607        if (
5608            self._match(TokenType.L_BRACE, advance=False)
5609            and self._next
5610            and self._next.text.upper() == "FN"
5611        ):
5612            self._advance(2)
5613            fn_syntax = True
5614
5615        func = self._parse_function_call(
5616            functions=functions,
5617            anonymous=anonymous,
5618            optional_parens=optional_parens,
5619            any_token=any_token,
5620        )
5621
5622        if fn_syntax:
5623            self._match(TokenType.R_BRACE)
5624
5625        return func
5626
5627    def _parse_function_call(
5628        self,
5629        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5630        anonymous: bool = False,
5631        optional_parens: bool = True,
5632        any_token: bool = False,
5633    ) -> t.Optional[exp.Expression]:
5634        if not self._curr:
5635            return None
5636
5637        comments = self._curr.comments
5638        token = self._curr
5639        token_type = self._curr.token_type
5640        this = self._curr.text
5641        upper = this.upper()
5642
5643        parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper)
5644        if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS:
5645            self._advance()
5646            return self._parse_window(parser(self))
5647
5648        if not self._next or self._next.token_type != TokenType.L_PAREN:
5649            if optional_parens and token_type in self.NO_PAREN_FUNCTIONS:
5650                self._advance()
5651                return self.expression(self.NO_PAREN_FUNCTIONS[token_type])
5652
5653            return None
5654
5655        if any_token:
5656            if token_type in self.RESERVED_TOKENS:
5657                return None
5658        elif token_type not in self.FUNC_TOKENS:
5659            return None
5660
5661        self._advance(2)
5662
5663        parser = self.FUNCTION_PARSERS.get(upper)
5664        if parser and not anonymous:
5665            this = parser(self)
5666        else:
5667            subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type)
5668
5669            if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH):
5670                this = self.expression(
5671                    subquery_predicate, comments=comments, this=self._parse_select()
5672                )
5673                self._match_r_paren()
5674                return this
5675
5676            if functions is None:
5677                functions = self.FUNCTIONS
5678
5679            function = functions.get(upper)
5680            known_function = function and not anonymous
5681
5682            alias = not known_function or upper in self.FUNCTIONS_WITH_ALIASED_ARGS
5683            args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
5684
5685            post_func_comments = self._curr and self._curr.comments
5686            if known_function and post_func_comments:
5687                # If the user-inputted comment "/* sqlglot.anonymous */" is following the function
5688                # call we'll construct it as exp.Anonymous, even if it's "known"
5689                if any(
5690                    comment.lstrip().startswith(exp.SQLGLOT_ANONYMOUS)
5691                    for comment in post_func_comments
5692                ):
5693                    known_function = False
5694
5695            if alias and known_function:
5696                args = self._kv_to_prop_eq(args)
5697
5698            if known_function:
5699                func_builder = t.cast(t.Callable, function)
5700
5701                if "dialect" in func_builder.__code__.co_varnames:
5702                    func = func_builder(args, dialect=self.dialect)
5703                else:
5704                    func = func_builder(args)
5705
5706                func = self.validate_expression(func, args)
5707                if self.dialect.PRESERVE_ORIGINAL_NAMES:
5708                    func.meta["name"] = this
5709
5710                this = func
5711            else:
5712                if token_type == TokenType.IDENTIFIER:
5713                    this = exp.Identifier(this=this, quoted=True).update_positions(token)
5714                this = self.expression(exp.Anonymous, this=this, expressions=args)
5715
5716        if isinstance(this, exp.Expression):
5717            this.add_comments(comments)
5718
5719        self._match_r_paren(this)
5720        return self._parse_window(this)
5721
5722    def _to_prop_eq(self, expression: exp.Expression, index: int) -> exp.Expression:
5723        return expression
5724
5725    def _kv_to_prop_eq(self, expressions: t.List[exp.Expression]) -> t.List[exp.Expression]:
5726        transformed = []
5727
5728        for index, e in enumerate(expressions):
5729            if isinstance(e, self.KEY_VALUE_DEFINITIONS):
5730                if isinstance(e, exp.Alias):
5731                    e = self.expression(exp.PropertyEQ, this=e.args.get("alias"), expression=e.this)
5732
5733                if not isinstance(e, exp.PropertyEQ):
5734                    e = self.expression(
5735                        exp.PropertyEQ, this=exp.to_identifier(e.this.name), expression=e.expression
5736                    )
5737
5738                if isinstance(e.this, exp.Column):
5739                    e.this.replace(e.this.this)
5740            else:
5741                e = self._to_prop_eq(e, index)
5742
5743            transformed.append(e)
5744
5745        return transformed
5746
5747    def _parse_user_defined_function_expression(self) -> t.Optional[exp.Expression]:
5748        return self._parse_statement()
5749
5750    def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
5751        return self._parse_column_def(this=self._parse_id_var(), computed_column=False)
5752
5753    def _parse_user_defined_function(
5754        self, kind: t.Optional[TokenType] = None
5755    ) -> t.Optional[exp.Expression]:
5756        this = self._parse_table_parts(schema=True)
5757
5758        if not self._match(TokenType.L_PAREN):
5759            return this
5760
5761        expressions = self._parse_csv(self._parse_function_parameter)
5762        self._match_r_paren()
5763        return self.expression(
5764            exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True
5765        )
5766
5767    def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier:
5768        literal = self._parse_primary()
5769        if literal:
5770            return self.expression(exp.Introducer, this=token.text, expression=literal)
5771
5772        return self._identifier_expression(token)
5773
5774    def _parse_session_parameter(self) -> exp.SessionParameter:
5775        kind = None
5776        this = self._parse_id_var() or self._parse_primary()
5777
5778        if this and self._match(TokenType.DOT):
5779            kind = this.name
5780            this = self._parse_var() or self._parse_primary()
5781
5782        return self.expression(exp.SessionParameter, this=this, kind=kind)
5783
5784    def _parse_lambda_arg(self) -> t.Optional[exp.Expression]:
5785        return self._parse_id_var()
5786
5787    def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]:
5788        index = self._index
5789
5790        if self._match(TokenType.L_PAREN):
5791            expressions = t.cast(
5792                t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_lambda_arg)
5793            )
5794
5795            if not self._match(TokenType.R_PAREN):
5796                self._retreat(index)
5797        else:
5798            expressions = [self._parse_lambda_arg()]
5799
5800        if self._match_set(self.LAMBDAS):
5801            return self.LAMBDAS[self._prev.token_type](self, expressions)
5802
5803        self._retreat(index)
5804
5805        this: t.Optional[exp.Expression]
5806
5807        if self._match(TokenType.DISTINCT):
5808            this = self.expression(
5809                exp.Distinct, expressions=self._parse_csv(self._parse_assignment)
5810            )
5811        else:
5812            this = self._parse_select_or_expression(alias=alias)
5813
5814        return self._parse_limit(
5815            self._parse_order(self._parse_having_max(self._parse_respect_or_ignore_nulls(this)))
5816        )
5817
5818    def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
5819        index = self._index
5820        if not self._match(TokenType.L_PAREN):
5821            return this
5822
5823        # Disambiguate between schema and subquery/CTE, e.g. in INSERT INTO table (<expr>),
5824        # expr can be of both types
5825        if self._match_set(self.SELECT_START_TOKENS):
5826            self._retreat(index)
5827            return this
5828        args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def())
5829        self._match_r_paren()
5830        return self.expression(exp.Schema, this=this, expressions=args)
5831
5832    def _parse_field_def(self) -> t.Optional[exp.Expression]:
5833        return self._parse_column_def(self._parse_field(any_token=True))
5834
5835    def _parse_column_def(
5836        self, this: t.Optional[exp.Expression], computed_column: bool = True
5837    ) -> t.Optional[exp.Expression]:
5838        # column defs are not really columns, they're identifiers
5839        if isinstance(this, exp.Column):
5840            this = this.this
5841
5842        if not computed_column:
5843            self._match(TokenType.ALIAS)
5844
5845        kind = self._parse_types(schema=True)
5846
5847        if self._match_text_seq("FOR", "ORDINALITY"):
5848            return self.expression(exp.ColumnDef, this=this, ordinality=True)
5849
5850        constraints: t.List[exp.Expression] = []
5851
5852        if (not kind and self._match(TokenType.ALIAS)) or self._match_texts(
5853            ("ALIAS", "MATERIALIZED")
5854        ):
5855            persisted = self._prev.text.upper() == "MATERIALIZED"
5856            constraint_kind = exp.ComputedColumnConstraint(
5857                this=self._parse_assignment(),
5858                persisted=persisted or self._match_text_seq("PERSISTED"),
5859                not_null=self._match_pair(TokenType.NOT, TokenType.NULL),
5860            )
5861            constraints.append(self.expression(exp.ColumnConstraint, kind=constraint_kind))
5862        elif (
5863            kind
5864            and self._match(TokenType.ALIAS, advance=False)
5865            and (
5866                not self.WRAPPED_TRANSFORM_COLUMN_CONSTRAINT
5867                or (self._next and self._next.token_type == TokenType.L_PAREN)
5868            )
5869        ):
5870            self._advance()
5871            constraints.append(
5872                self.expression(
5873                    exp.ColumnConstraint,
5874                    kind=exp.TransformColumnConstraint(this=self._parse_disjunction()),
5875                )
5876            )
5877
5878        while True:
5879            constraint = self._parse_column_constraint()
5880            if not constraint:
5881                break
5882            constraints.append(constraint)
5883
5884        if not kind and not constraints:
5885            return this
5886
5887        return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints)
5888
5889    def _parse_auto_increment(
5890        self,
5891    ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint:
5892        start = None
5893        increment = None
5894
5895        if self._match(TokenType.L_PAREN, advance=False):
5896            args = self._parse_wrapped_csv(self._parse_bitwise)
5897            start = seq_get(args, 0)
5898            increment = seq_get(args, 1)
5899        elif self._match_text_seq("START"):
5900            start = self._parse_bitwise()
5901            self._match_text_seq("INCREMENT")
5902            increment = self._parse_bitwise()
5903
5904        if start and increment:
5905            return exp.GeneratedAsIdentityColumnConstraint(
5906                start=start, increment=increment, this=False
5907            )
5908
5909        return exp.AutoIncrementColumnConstraint()
5910
5911    def _parse_auto_property(self) -> t.Optional[exp.AutoRefreshProperty]:
5912        if not self._match_text_seq("REFRESH"):
5913            self._retreat(self._index - 1)
5914            return None
5915        return self.expression(exp.AutoRefreshProperty, this=self._parse_var(upper=True))
5916
5917    def _parse_compress(self) -> exp.CompressColumnConstraint:
5918        if self._match(TokenType.L_PAREN, advance=False):
5919            return self.expression(
5920                exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise)
5921            )
5922
5923        return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise())
5924
5925    def _parse_generated_as_identity(
5926        self,
5927    ) -> (
5928        exp.GeneratedAsIdentityColumnConstraint
5929        | exp.ComputedColumnConstraint
5930        | exp.GeneratedAsRowColumnConstraint
5931    ):
5932        if self._match_text_seq("BY", "DEFAULT"):
5933            on_null = self._match_pair(TokenType.ON, TokenType.NULL)
5934            this = self.expression(
5935                exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null
5936            )
5937        else:
5938            self._match_text_seq("ALWAYS")
5939            this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True)
5940
5941        self._match(TokenType.ALIAS)
5942
5943        if self._match_text_seq("ROW"):
5944            start = self._match_text_seq("START")
5945            if not start:
5946                self._match(TokenType.END)
5947            hidden = self._match_text_seq("HIDDEN")
5948            return self.expression(exp.GeneratedAsRowColumnConstraint, start=start, hidden=hidden)
5949
5950        identity = self._match_text_seq("IDENTITY")
5951
5952        if self._match(TokenType.L_PAREN):
5953            if self._match(TokenType.START_WITH):
5954                this.set("start", self._parse_bitwise())
5955            if self._match_text_seq("INCREMENT", "BY"):
5956                this.set("increment", self._parse_bitwise())
5957            if self._match_text_seq("MINVALUE"):
5958                this.set("minvalue", self._parse_bitwise())
5959            if self._match_text_seq("MAXVALUE"):
5960                this.set("maxvalue", self._parse_bitwise())
5961
5962            if self._match_text_seq("CYCLE"):
5963                this.set("cycle", True)
5964            elif self._match_text_seq("NO", "CYCLE"):
5965                this.set("cycle", False)
5966
5967            if not identity:
5968                this.set("expression", self._parse_range())
5969            elif not this.args.get("start") and self._match(TokenType.NUMBER, advance=False):
5970                args = self._parse_csv(self._parse_bitwise)
5971                this.set("start", seq_get(args, 0))
5972                this.set("increment", seq_get(args, 1))
5973
5974            self._match_r_paren()
5975
5976        return this
5977
5978    def _parse_inline(self) -> exp.InlineLengthColumnConstraint:
5979        self._match_text_seq("LENGTH")
5980        return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise())
5981
5982    def _parse_not_constraint(self) -> t.Optional[exp.Expression]:
5983        if self._match_text_seq("NULL"):
5984            return self.expression(exp.NotNullColumnConstraint)
5985        if self._match_text_seq("CASESPECIFIC"):
5986            return self.expression(exp.CaseSpecificColumnConstraint, not_=True)
5987        if self._match_text_seq("FOR", "REPLICATION"):
5988            return self.expression(exp.NotForReplicationColumnConstraint)
5989
5990        # Unconsume the `NOT` token
5991        self._retreat(self._index - 1)
5992        return None
5993
5994    def _parse_column_constraint(self) -> t.Optional[exp.Expression]:
5995        this = self._match(TokenType.CONSTRAINT) and self._parse_id_var()
5996
5997        procedure_option_follows = (
5998            self._match(TokenType.WITH, advance=False)
5999            and self._next
6000            and self._next.text.upper() in self.PROCEDURE_OPTIONS
6001        )
6002
6003        if not procedure_option_follows and self._match_texts(self.CONSTRAINT_PARSERS):
6004            return self.expression(
6005                exp.ColumnConstraint,
6006                this=this,
6007                kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self),
6008            )
6009
6010        return this
6011
6012    def _parse_constraint(self) -> t.Optional[exp.Expression]:
6013        if not self._match(TokenType.CONSTRAINT):
6014            return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS)
6015
6016        return self.expression(
6017            exp.Constraint,
6018            this=self._parse_id_var(),
6019            expressions=self._parse_unnamed_constraints(),
6020        )
6021
6022    def _parse_unnamed_constraints(self) -> t.List[exp.Expression]:
6023        constraints = []
6024        while True:
6025            constraint = self._parse_unnamed_constraint() or self._parse_function()
6026            if not constraint:
6027                break
6028            constraints.append(constraint)
6029
6030        return constraints
6031
6032    def _parse_unnamed_constraint(
6033        self, constraints: t.Optional[t.Collection[str]] = None
6034    ) -> t.Optional[exp.Expression]:
6035        if self._match(TokenType.IDENTIFIER, advance=False) or not self._match_texts(
6036            constraints or self.CONSTRAINT_PARSERS
6037        ):
6038            return None
6039
6040        constraint = self._prev.text.upper()
6041        if constraint not in self.CONSTRAINT_PARSERS:
6042            self.raise_error(f"No parser found for schema constraint {constraint}.")
6043
6044        return self.CONSTRAINT_PARSERS[constraint](self)
6045
6046    def _parse_unique_key(self) -> t.Optional[exp.Expression]:
6047        return self._parse_id_var(any_token=False)
6048
6049    def _parse_unique(self) -> exp.UniqueColumnConstraint:
6050        self._match_text_seq("KEY")
6051        return self.expression(
6052            exp.UniqueColumnConstraint,
6053            nulls=self._match_text_seq("NULLS", "NOT", "DISTINCT"),
6054            this=self._parse_schema(self._parse_unique_key()),
6055            index_type=self._match(TokenType.USING) and self._advance_any() and self._prev.text,
6056            on_conflict=self._parse_on_conflict(),
6057            options=self._parse_key_constraint_options(),
6058        )
6059
6060    def _parse_key_constraint_options(self) -> t.List[str]:
6061        options = []
6062        while True:
6063            if not self._curr:
6064                break
6065
6066            if self._match(TokenType.ON):
6067                action = None
6068                on = self._advance_any() and self._prev.text
6069
6070                if self._match_text_seq("NO", "ACTION"):
6071                    action = "NO ACTION"
6072                elif self._match_text_seq("CASCADE"):
6073                    action = "CASCADE"
6074                elif self._match_text_seq("RESTRICT"):
6075                    action = "RESTRICT"
6076                elif self._match_pair(TokenType.SET, TokenType.NULL):
6077                    action = "SET NULL"
6078                elif self._match_pair(TokenType.SET, TokenType.DEFAULT):
6079                    action = "SET DEFAULT"
6080                else:
6081                    self.raise_error("Invalid key constraint")
6082
6083                options.append(f"ON {on} {action}")
6084            else:
6085                var = self._parse_var_from_options(
6086                    self.KEY_CONSTRAINT_OPTIONS, raise_unmatched=False
6087                )
6088                if not var:
6089                    break
6090                options.append(var.name)
6091
6092        return options
6093
6094    def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]:
6095        if match and not self._match(TokenType.REFERENCES):
6096            return None
6097
6098        expressions = None
6099        this = self._parse_table(schema=True)
6100        options = self._parse_key_constraint_options()
6101        return self.expression(exp.Reference, this=this, expressions=expressions, options=options)
6102
6103    def _parse_foreign_key(self) -> exp.ForeignKey:
6104        expressions = (
6105            self._parse_wrapped_id_vars()
6106            if not self._match(TokenType.REFERENCES, advance=False)
6107            else None
6108        )
6109        reference = self._parse_references()
6110        on_options = {}
6111
6112        while self._match(TokenType.ON):
6113            if not self._match_set((TokenType.DELETE, TokenType.UPDATE)):
6114                self.raise_error("Expected DELETE or UPDATE")
6115
6116            kind = self._prev.text.lower()
6117
6118            if self._match_text_seq("NO", "ACTION"):
6119                action = "NO ACTION"
6120            elif self._match(TokenType.SET):
6121                self._match_set((TokenType.NULL, TokenType.DEFAULT))
6122                action = "SET " + self._prev.text.upper()
6123            else:
6124                self._advance()
6125                action = self._prev.text.upper()
6126
6127            on_options[kind] = action
6128
6129        return self.expression(
6130            exp.ForeignKey,
6131            expressions=expressions,
6132            reference=reference,
6133            options=self._parse_key_constraint_options(),
6134            **on_options,  # type: ignore
6135        )
6136
6137    def _parse_primary_key_part(self) -> t.Optional[exp.Expression]:
6138        return self._parse_ordered() or self._parse_field()
6139
6140    def _parse_period_for_system_time(self) -> t.Optional[exp.PeriodForSystemTimeConstraint]:
6141        if not self._match(TokenType.TIMESTAMP_SNAPSHOT):
6142            self._retreat(self._index - 1)
6143            return None
6144
6145        id_vars = self._parse_wrapped_id_vars()
6146        return self.expression(
6147            exp.PeriodForSystemTimeConstraint,
6148            this=seq_get(id_vars, 0),
6149            expression=seq_get(id_vars, 1),
6150        )
6151
6152    def _parse_primary_key(
6153        self, wrapped_optional: bool = False, in_props: bool = False
6154    ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey:
6155        desc = (
6156            self._match_set((TokenType.ASC, TokenType.DESC))
6157            and self._prev.token_type == TokenType.DESC
6158        )
6159
6160        if not in_props and not self._match(TokenType.L_PAREN, advance=False):
6161            return self.expression(
6162                exp.PrimaryKeyColumnConstraint,
6163                desc=desc,
6164                options=self._parse_key_constraint_options(),
6165            )
6166
6167        expressions = self._parse_wrapped_csv(
6168            self._parse_primary_key_part, optional=wrapped_optional
6169        )
6170        options = self._parse_key_constraint_options()
6171        return self.expression(exp.PrimaryKey, expressions=expressions, options=options)
6172
6173    def _parse_bracket_key_value(self, is_map: bool = False) -> t.Optional[exp.Expression]:
6174        return self._parse_slice(self._parse_alias(self._parse_assignment(), explicit=True))
6175
6176    def _parse_odbc_datetime_literal(self) -> exp.Expression:
6177        """
6178        Parses a datetime column in ODBC format. We parse the column into the corresponding
6179        types, for example `{d'yyyy-mm-dd'}` will be parsed as a `Date` column, exactly the
6180        same as we did for `DATE('yyyy-mm-dd')`.
6181
6182        Reference:
6183        https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals
6184        """
6185        self._match(TokenType.VAR)
6186        exp_class = self.ODBC_DATETIME_LITERALS[self._prev.text.lower()]
6187        expression = self.expression(exp_class=exp_class, this=self._parse_string())
6188        if not self._match(TokenType.R_BRACE):
6189            self.raise_error("Expected }")
6190        return expression
6191
6192    def _parse_bracket(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
6193        if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)):
6194            return this
6195
6196        bracket_kind = self._prev.token_type
6197        if (
6198            bracket_kind == TokenType.L_BRACE
6199            and self._curr
6200            and self._curr.token_type == TokenType.VAR
6201            and self._curr.text.lower() in self.ODBC_DATETIME_LITERALS
6202        ):
6203            return self._parse_odbc_datetime_literal()
6204
6205        expressions = self._parse_csv(
6206            lambda: self._parse_bracket_key_value(is_map=bracket_kind == TokenType.L_BRACE)
6207        )
6208
6209        if bracket_kind == TokenType.L_BRACKET and not self._match(TokenType.R_BRACKET):
6210            self.raise_error("Expected ]")
6211        elif bracket_kind == TokenType.L_BRACE and not self._match(TokenType.R_BRACE):
6212            self.raise_error("Expected }")
6213
6214        # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
6215        if bracket_kind == TokenType.L_BRACE:
6216            this = self.expression(exp.Struct, expressions=self._kv_to_prop_eq(expressions))
6217        elif not this:
6218            this = build_array_constructor(
6219                exp.Array, args=expressions, bracket_kind=bracket_kind, dialect=self.dialect
6220            )
6221        else:
6222            constructor_type = self.ARRAY_CONSTRUCTORS.get(this.name.upper())
6223            if constructor_type:
6224                return build_array_constructor(
6225                    constructor_type,
6226                    args=expressions,
6227                    bracket_kind=bracket_kind,
6228                    dialect=self.dialect,
6229                )
6230
6231            expressions = apply_index_offset(
6232                this, expressions, -self.dialect.INDEX_OFFSET, dialect=self.dialect
6233            )
6234            this = self.expression(exp.Bracket, this=this, expressions=expressions)
6235
6236        self._add_comments(this)
6237        return self._parse_bracket(this)
6238
6239    def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6240        if self._match(TokenType.COLON):
6241            return self.expression(exp.Slice, this=this, expression=self._parse_assignment())
6242        return this
6243
6244    def _parse_case(self) -> t.Optional[exp.Expression]:
6245        ifs = []
6246        default = None
6247
6248        comments = self._prev_comments
6249        expression = self._parse_assignment()
6250
6251        while self._match(TokenType.WHEN):
6252            this = self._parse_assignment()
6253            self._match(TokenType.THEN)
6254            then = self._parse_assignment()
6255            ifs.append(self.expression(exp.If, this=this, true=then))
6256
6257        if self._match(TokenType.ELSE):
6258            default = self._parse_assignment()
6259
6260        if not self._match(TokenType.END):
6261            if isinstance(default, exp.Interval) and default.this.sql().upper() == "END":
6262                default = exp.column("interval")
6263            else:
6264                self.raise_error("Expected END after CASE", self._prev)
6265
6266        return self.expression(
6267            exp.Case, comments=comments, this=expression, ifs=ifs, default=default
6268        )
6269
6270    def _parse_if(self) -> t.Optional[exp.Expression]:
6271        if self._match(TokenType.L_PAREN):
6272            args = self._parse_csv(
6273                lambda: self._parse_alias(self._parse_assignment(), explicit=True)
6274            )
6275            this = self.validate_expression(exp.If.from_arg_list(args), args)
6276            self._match_r_paren()
6277        else:
6278            index = self._index - 1
6279
6280            if self.NO_PAREN_IF_COMMANDS and index == 0:
6281                return self._parse_as_command(self._prev)
6282
6283            condition = self._parse_assignment()
6284
6285            if not condition:
6286                self._retreat(index)
6287                return None
6288
6289            self._match(TokenType.THEN)
6290            true = self._parse_assignment()
6291            false = self._parse_assignment() if self._match(TokenType.ELSE) else None
6292            self._match(TokenType.END)
6293            this = self.expression(exp.If, this=condition, true=true, false=false)
6294
6295        return this
6296
6297    def _parse_next_value_for(self) -> t.Optional[exp.Expression]:
6298        if not self._match_text_seq("VALUE", "FOR"):
6299            self._retreat(self._index - 1)
6300            return None
6301
6302        return self.expression(
6303            exp.NextValueFor,
6304            this=self._parse_column(),
6305            order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order),
6306        )
6307
6308    def _parse_extract(self) -> exp.Extract | exp.Anonymous:
6309        this = self._parse_function() or self._parse_var_or_string(upper=True)
6310
6311        if self._match(TokenType.FROM):
6312            return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6313
6314        if not self._match(TokenType.COMMA):
6315            self.raise_error("Expected FROM or comma after EXTRACT", self._prev)
6316
6317        return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6318
6319    def _parse_gap_fill(self) -> exp.GapFill:
6320        self._match(TokenType.TABLE)
6321        this = self._parse_table()
6322
6323        self._match(TokenType.COMMA)
6324        args = [this, *self._parse_csv(self._parse_lambda)]
6325
6326        gap_fill = exp.GapFill.from_arg_list(args)
6327        return self.validate_expression(gap_fill, args)
6328
6329    def _parse_cast(self, strict: bool, safe: t.Optional[bool] = None) -> exp.Expression:
6330        this = self._parse_assignment()
6331
6332        if not self._match(TokenType.ALIAS):
6333            if self._match(TokenType.COMMA):
6334                return self.expression(exp.CastToStrType, this=this, to=self._parse_string())
6335
6336            self.raise_error("Expected AS after CAST")
6337
6338        fmt = None
6339        to = self._parse_types()
6340
6341        default = self._match(TokenType.DEFAULT)
6342        if default:
6343            default = self._parse_bitwise()
6344            self._match_text_seq("ON", "CONVERSION", "ERROR")
6345
6346        if self._match_set((TokenType.FORMAT, TokenType.COMMA)):
6347            fmt_string = self._parse_string()
6348            fmt = self._parse_at_time_zone(fmt_string)
6349
6350            if not to:
6351                to = exp.DataType.build(exp.DataType.Type.UNKNOWN)
6352            if to.this in exp.DataType.TEMPORAL_TYPES:
6353                this = self.expression(
6354                    exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime,
6355                    this=this,
6356                    format=exp.Literal.string(
6357                        format_time(
6358                            fmt_string.this if fmt_string else "",
6359                            self.dialect.FORMAT_MAPPING or self.dialect.TIME_MAPPING,
6360                            self.dialect.FORMAT_TRIE or self.dialect.TIME_TRIE,
6361                        )
6362                    ),
6363                    safe=safe,
6364                )
6365
6366                if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime):
6367                    this.set("zone", fmt.args["zone"])
6368                return this
6369        elif not to:
6370            self.raise_error("Expected TYPE after CAST")
6371        elif isinstance(to, exp.Identifier):
6372            to = exp.DataType.build(to.name, udt=True)
6373        elif to.this == exp.DataType.Type.CHAR:
6374            if self._match(TokenType.CHARACTER_SET):
6375                to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
6376
6377        return self.expression(
6378            exp.Cast if strict else exp.TryCast,
6379            this=this,
6380            to=to,
6381            format=fmt,
6382            safe=safe,
6383            action=self._parse_var_from_options(self.CAST_ACTIONS, raise_unmatched=False),
6384            default=default,
6385        )
6386
6387    def _parse_string_agg(self) -> exp.GroupConcat:
6388        if self._match(TokenType.DISTINCT):
6389            args: t.List[t.Optional[exp.Expression]] = [
6390                self.expression(exp.Distinct, expressions=[self._parse_assignment()])
6391            ]
6392            if self._match(TokenType.COMMA):
6393                args.extend(self._parse_csv(self._parse_assignment))
6394        else:
6395            args = self._parse_csv(self._parse_assignment)  # type: ignore
6396
6397        if self._match_text_seq("ON", "OVERFLOW"):
6398            # trino: LISTAGG(expression [, separator] [ON OVERFLOW overflow_behavior])
6399            if self._match_text_seq("ERROR"):
6400                on_overflow: t.Optional[exp.Expression] = exp.var("ERROR")
6401            else:
6402                self._match_text_seq("TRUNCATE")
6403                on_overflow = self.expression(
6404                    exp.OverflowTruncateBehavior,
6405                    this=self._parse_string(),
6406                    with_count=(
6407                        self._match_text_seq("WITH", "COUNT")
6408                        or not self._match_text_seq("WITHOUT", "COUNT")
6409                    ),
6410                )
6411        else:
6412            on_overflow = None
6413
6414        index = self._index
6415        if not self._match(TokenType.R_PAREN) and args:
6416            # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]])
6417            # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n])
6418            # The order is parsed through `this` as a canonicalization for WITHIN GROUPs
6419            args[0] = self._parse_limit(this=self._parse_order(this=args[0]))
6420            return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1))
6421
6422        # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]).
6423        # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that
6424        # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them.
6425        if not self._match_text_seq("WITHIN", "GROUP"):
6426            self._retreat(index)
6427            return self.validate_expression(exp.GroupConcat.from_arg_list(args), args)
6428
6429        # The corresponding match_r_paren will be called in parse_function (caller)
6430        self._match_l_paren()
6431
6432        return self.expression(
6433            exp.GroupConcat,
6434            this=self._parse_order(this=seq_get(args, 0)),
6435            separator=seq_get(args, 1),
6436            on_overflow=on_overflow,
6437        )
6438
6439    def _parse_convert(
6440        self, strict: bool, safe: t.Optional[bool] = None
6441    ) -> t.Optional[exp.Expression]:
6442        this = self._parse_bitwise()
6443
6444        if self._match(TokenType.USING):
6445            to: t.Optional[exp.Expression] = self.expression(
6446                exp.CharacterSet, this=self._parse_var()
6447            )
6448        elif self._match(TokenType.COMMA):
6449            to = self._parse_types()
6450        else:
6451            to = None
6452
6453        return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, safe=safe)
6454
6455    def _parse_xml_table(self) -> exp.XMLTable:
6456        namespaces = None
6457        passing = None
6458        columns = None
6459
6460        if self._match_text_seq("XMLNAMESPACES", "("):
6461            namespaces = self._parse_xml_namespace()
6462            self._match_text_seq(")", ",")
6463
6464        this = self._parse_string()
6465
6466        if self._match_text_seq("PASSING"):
6467            # The BY VALUE keywords are optional and are provided for semantic clarity
6468            self._match_text_seq("BY", "VALUE")
6469            passing = self._parse_csv(self._parse_column)
6470
6471        by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
6472
6473        if self._match_text_seq("COLUMNS"):
6474            columns = self._parse_csv(self._parse_field_def)
6475
6476        return self.expression(
6477            exp.XMLTable,
6478            this=this,
6479            namespaces=namespaces,
6480            passing=passing,
6481            columns=columns,
6482            by_ref=by_ref,
6483        )
6484
6485    def _parse_xml_namespace(self) -> t.List[exp.XMLNamespace]:
6486        namespaces = []
6487
6488        while True:
6489            if self._match(TokenType.DEFAULT):
6490                uri = self._parse_string()
6491            else:
6492                uri = self._parse_alias(self._parse_string())
6493            namespaces.append(self.expression(exp.XMLNamespace, this=uri))
6494            if not self._match(TokenType.COMMA):
6495                break
6496
6497        return namespaces
6498
6499    def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]:
6500        """
6501        There are generally two variants of the DECODE function:
6502
6503        - DECODE(bin, charset)
6504        - DECODE(expression, search, result [, search, result] ... [, default])
6505
6506        The second variant will always be parsed into a CASE expression. Note that NULL
6507        needs special treatment, since we need to explicitly check for it with `IS NULL`,
6508        instead of relying on pattern matching.
6509        """
6510        args = self._parse_csv(self._parse_assignment)
6511
6512        if len(args) < 3:
6513            return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1))
6514
6515        expression, *expressions = args
6516        if not expression:
6517            return None
6518
6519        ifs = []
6520        for search, result in zip(expressions[::2], expressions[1::2]):
6521            if not search or not result:
6522                return None
6523
6524            if isinstance(search, exp.Literal):
6525                ifs.append(
6526                    exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result)
6527                )
6528            elif isinstance(search, exp.Null):
6529                ifs.append(
6530                    exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result)
6531                )
6532            else:
6533                cond = exp.or_(
6534                    exp.EQ(this=expression.copy(), expression=search),
6535                    exp.and_(
6536                        exp.Is(this=expression.copy(), expression=exp.Null()),
6537                        exp.Is(this=search.copy(), expression=exp.Null()),
6538                        copy=False,
6539                    ),
6540                    copy=False,
6541                )
6542                ifs.append(exp.If(this=cond, true=result))
6543
6544        return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
6545
6546    def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]:
6547        self._match_text_seq("KEY")
6548        key = self._parse_column()
6549        self._match_set(self.JSON_KEY_VALUE_SEPARATOR_TOKENS)
6550        self._match_text_seq("VALUE")
6551        value = self._parse_bitwise()
6552
6553        if not key and not value:
6554            return None
6555        return self.expression(exp.JSONKeyValue, this=key, expression=value)
6556
6557    def _parse_format_json(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6558        if not this or not self._match_text_seq("FORMAT", "JSON"):
6559            return this
6560
6561        return self.expression(exp.FormatJson, this=this)
6562
6563    def _parse_on_condition(self) -> t.Optional[exp.OnCondition]:
6564        # MySQL uses "X ON EMPTY Y ON ERROR" (e.g. JSON_VALUE) while Oracle uses the opposite (e.g. JSON_EXISTS)
6565        if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR:
6566            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6567            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6568        else:
6569            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6570            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6571
6572        null = self._parse_on_handling("NULL", *self.ON_CONDITION_TOKENS)
6573
6574        if not empty and not error and not null:
6575            return None
6576
6577        return self.expression(
6578            exp.OnCondition,
6579            empty=empty,
6580            error=error,
6581            null=null,
6582        )
6583
6584    def _parse_on_handling(
6585        self, on: str, *values: str
6586    ) -> t.Optional[str] | t.Optional[exp.Expression]:
6587        # Parses the "X ON Y" or "DEFAULT <expr> ON Y syntax, e.g. NULL ON NULL (Oracle, T-SQL, MySQL)
6588        for value in values:
6589            if self._match_text_seq(value, "ON", on):
6590                return f"{value} ON {on}"
6591
6592        index = self._index
6593        if self._match(TokenType.DEFAULT):
6594            default_value = self._parse_bitwise()
6595            if self._match_text_seq("ON", on):
6596                return default_value
6597
6598            self._retreat(index)
6599
6600        return None
6601
6602    @t.overload
6603    def _parse_json_object(self, agg: Lit[False]) -> exp.JSONObject: ...
6604
6605    @t.overload
6606    def _parse_json_object(self, agg: Lit[True]) -> exp.JSONObjectAgg: ...
6607
6608    def _parse_json_object(self, agg=False):
6609        star = self._parse_star()
6610        expressions = (
6611            [star]
6612            if star
6613            else self._parse_csv(lambda: self._parse_format_json(self._parse_json_key_value()))
6614        )
6615        null_handling = self._parse_on_handling("NULL", "NULL", "ABSENT")
6616
6617        unique_keys = None
6618        if self._match_text_seq("WITH", "UNIQUE"):
6619            unique_keys = True
6620        elif self._match_text_seq("WITHOUT", "UNIQUE"):
6621            unique_keys = False
6622
6623        self._match_text_seq("KEYS")
6624
6625        return_type = self._match_text_seq("RETURNING") and self._parse_format_json(
6626            self._parse_type()
6627        )
6628        encoding = self._match_text_seq("ENCODING") and self._parse_var()
6629
6630        return self.expression(
6631            exp.JSONObjectAgg if agg else exp.JSONObject,
6632            expressions=expressions,
6633            null_handling=null_handling,
6634            unique_keys=unique_keys,
6635            return_type=return_type,
6636            encoding=encoding,
6637        )
6638
6639    # Note: this is currently incomplete; it only implements the "JSON_value_column" part
6640    def _parse_json_column_def(self) -> exp.JSONColumnDef:
6641        if not self._match_text_seq("NESTED"):
6642            this = self._parse_id_var()
6643            kind = self._parse_types(allow_identifiers=False)
6644            nested = None
6645        else:
6646            this = None
6647            kind = None
6648            nested = True
6649
6650        path = self._match_text_seq("PATH") and self._parse_string()
6651        nested_schema = nested and self._parse_json_schema()
6652
6653        return self.expression(
6654            exp.JSONColumnDef,
6655            this=this,
6656            kind=kind,
6657            path=path,
6658            nested_schema=nested_schema,
6659        )
6660
6661    def _parse_json_schema(self) -> exp.JSONSchema:
6662        self._match_text_seq("COLUMNS")
6663        return self.expression(
6664            exp.JSONSchema,
6665            expressions=self._parse_wrapped_csv(self._parse_json_column_def, optional=True),
6666        )
6667
6668    def _parse_json_table(self) -> exp.JSONTable:
6669        this = self._parse_format_json(self._parse_bitwise())
6670        path = self._match(TokenType.COMMA) and self._parse_string()
6671        error_handling = self._parse_on_handling("ERROR", "ERROR", "NULL")
6672        empty_handling = self._parse_on_handling("EMPTY", "ERROR", "NULL")
6673        schema = self._parse_json_schema()
6674
6675        return exp.JSONTable(
6676            this=this,
6677            schema=schema,
6678            path=path,
6679            error_handling=error_handling,
6680            empty_handling=empty_handling,
6681        )
6682
6683    def _parse_match_against(self) -> exp.MatchAgainst:
6684        expressions = self._parse_csv(self._parse_column)
6685
6686        self._match_text_seq(")", "AGAINST", "(")
6687
6688        this = self._parse_string()
6689
6690        if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"):
6691            modifier = "IN NATURAL LANGUAGE MODE"
6692            if self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6693                modifier = f"{modifier} WITH QUERY EXPANSION"
6694        elif self._match_text_seq("IN", "BOOLEAN", "MODE"):
6695            modifier = "IN BOOLEAN MODE"
6696        elif self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6697            modifier = "WITH QUERY EXPANSION"
6698        else:
6699            modifier = None
6700
6701        return self.expression(
6702            exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier
6703        )
6704
6705    # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16
6706    def _parse_open_json(self) -> exp.OpenJSON:
6707        this = self._parse_bitwise()
6708        path = self._match(TokenType.COMMA) and self._parse_string()
6709
6710        def _parse_open_json_column_def() -> exp.OpenJSONColumnDef:
6711            this = self._parse_field(any_token=True)
6712            kind = self._parse_types()
6713            path = self._parse_string()
6714            as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON)
6715
6716            return self.expression(
6717                exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json
6718            )
6719
6720        expressions = None
6721        if self._match_pair(TokenType.R_PAREN, TokenType.WITH):
6722            self._match_l_paren()
6723            expressions = self._parse_csv(_parse_open_json_column_def)
6724
6725        return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions)
6726
6727    def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition:
6728        args = self._parse_csv(self._parse_bitwise)
6729
6730        if self._match(TokenType.IN):
6731            return self.expression(
6732                exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0)
6733            )
6734
6735        if haystack_first:
6736            haystack = seq_get(args, 0)
6737            needle = seq_get(args, 1)
6738        else:
6739            haystack = seq_get(args, 1)
6740            needle = seq_get(args, 0)
6741
6742        return self.expression(
6743            exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2)
6744        )
6745
6746    def _parse_predict(self) -> exp.Predict:
6747        self._match_text_seq("MODEL")
6748        this = self._parse_table()
6749
6750        self._match(TokenType.COMMA)
6751        self._match_text_seq("TABLE")
6752
6753        return self.expression(
6754            exp.Predict,
6755            this=this,
6756            expression=self._parse_table(),
6757            params_struct=self._match(TokenType.COMMA) and self._parse_bitwise(),
6758        )
6759
6760    def _parse_join_hint(self, func_name: str) -> exp.JoinHint:
6761        args = self._parse_csv(self._parse_table)
6762        return exp.JoinHint(this=func_name.upper(), expressions=args)
6763
6764    def _parse_substring(self) -> exp.Substring:
6765        # Postgres supports the form: substring(string [from int] [for int])
6766        # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6
6767
6768        args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise))
6769
6770        if self._match(TokenType.FROM):
6771            args.append(self._parse_bitwise())
6772        if self._match(TokenType.FOR):
6773            if len(args) == 1:
6774                args.append(exp.Literal.number(1))
6775            args.append(self._parse_bitwise())
6776
6777        return self.validate_expression(exp.Substring.from_arg_list(args), args)
6778
6779    def _parse_trim(self) -> exp.Trim:
6780        # https://www.w3resource.com/sql/character-functions/trim.php
6781        # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html
6782
6783        position = None
6784        collation = None
6785        expression = None
6786
6787        if self._match_texts(self.TRIM_TYPES):
6788            position = self._prev.text.upper()
6789
6790        this = self._parse_bitwise()
6791        if self._match_set((TokenType.FROM, TokenType.COMMA)):
6792            invert_order = self._prev.token_type == TokenType.FROM or self.TRIM_PATTERN_FIRST
6793            expression = self._parse_bitwise()
6794
6795            if invert_order:
6796                this, expression = expression, this
6797
6798        if self._match(TokenType.COLLATE):
6799            collation = self._parse_bitwise()
6800
6801        return self.expression(
6802            exp.Trim, this=this, position=position, expression=expression, collation=collation
6803        )
6804
6805    def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]:
6806        return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window)
6807
6808    def _parse_named_window(self) -> t.Optional[exp.Expression]:
6809        return self._parse_window(self._parse_id_var(), alias=True)
6810
6811    def _parse_respect_or_ignore_nulls(
6812        self, this: t.Optional[exp.Expression]
6813    ) -> t.Optional[exp.Expression]:
6814        if self._match_text_seq("IGNORE", "NULLS"):
6815            return self.expression(exp.IgnoreNulls, this=this)
6816        if self._match_text_seq("RESPECT", "NULLS"):
6817            return self.expression(exp.RespectNulls, this=this)
6818        return this
6819
6820    def _parse_having_max(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6821        if self._match(TokenType.HAVING):
6822            self._match_texts(("MAX", "MIN"))
6823            max = self._prev.text.upper() != "MIN"
6824            return self.expression(
6825                exp.HavingMax, this=this, expression=self._parse_column(), max=max
6826            )
6827
6828        return this
6829
6830    def _parse_window(
6831        self, this: t.Optional[exp.Expression], alias: bool = False
6832    ) -> t.Optional[exp.Expression]:
6833        func = this
6834        comments = func.comments if isinstance(func, exp.Expression) else None
6835
6836        # T-SQL allows the OVER (...) syntax after WITHIN GROUP.
6837        # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16
6838        if self._match_text_seq("WITHIN", "GROUP"):
6839            order = self._parse_wrapped(self._parse_order)
6840            this = self.expression(exp.WithinGroup, this=this, expression=order)
6841
6842        if self._match_pair(TokenType.FILTER, TokenType.L_PAREN):
6843            self._match(TokenType.WHERE)
6844            this = self.expression(
6845                exp.Filter, this=this, expression=self._parse_where(skip_where_token=True)
6846            )
6847            self._match_r_paren()
6848
6849        # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER
6850        # Some dialects choose to implement and some do not.
6851        # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
6852
6853        # There is some code above in _parse_lambda that handles
6854        #   SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ...
6855
6856        # The below changes handle
6857        #   SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ...
6858
6859        # Oracle allows both formats
6860        #   (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html)
6861        #   and Snowflake chose to do the same for familiarity
6862        #   https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes
6863        if isinstance(this, exp.AggFunc):
6864            ignore_respect = this.find(exp.IgnoreNulls, exp.RespectNulls)
6865
6866            if ignore_respect and ignore_respect is not this:
6867                ignore_respect.replace(ignore_respect.this)
6868                this = self.expression(ignore_respect.__class__, this=this)
6869
6870        this = self._parse_respect_or_ignore_nulls(this)
6871
6872        # bigquery select from window x AS (partition by ...)
6873        if alias:
6874            over = None
6875            self._match(TokenType.ALIAS)
6876        elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS):
6877            return this
6878        else:
6879            over = self._prev.text.upper()
6880
6881        if comments and isinstance(func, exp.Expression):
6882            func.pop_comments()
6883
6884        if not self._match(TokenType.L_PAREN):
6885            return self.expression(
6886                exp.Window,
6887                comments=comments,
6888                this=this,
6889                alias=self._parse_id_var(False),
6890                over=over,
6891            )
6892
6893        window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS)
6894
6895        first = self._match(TokenType.FIRST)
6896        if self._match_text_seq("LAST"):
6897            first = False
6898
6899        partition, order = self._parse_partition_and_order()
6900        kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text
6901
6902        if kind:
6903            self._match(TokenType.BETWEEN)
6904            start = self._parse_window_spec()
6905            self._match(TokenType.AND)
6906            end = self._parse_window_spec()
6907            exclude = (
6908                self._parse_var_from_options(self.WINDOW_EXCLUDE_OPTIONS)
6909                if self._match_text_seq("EXCLUDE")
6910                else None
6911            )
6912
6913            spec = self.expression(
6914                exp.WindowSpec,
6915                kind=kind,
6916                start=start["value"],
6917                start_side=start["side"],
6918                end=end["value"],
6919                end_side=end["side"],
6920                exclude=exclude,
6921            )
6922        else:
6923            spec = None
6924
6925        self._match_r_paren()
6926
6927        window = self.expression(
6928            exp.Window,
6929            comments=comments,
6930            this=this,
6931            partition_by=partition,
6932            order=order,
6933            spec=spec,
6934            alias=window_alias,
6935            over=over,
6936            first=first,
6937        )
6938
6939        # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...)
6940        if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False):
6941            return self._parse_window(window, alias=alias)
6942
6943        return window
6944
6945    def _parse_partition_and_order(
6946        self,
6947    ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]:
6948        return self._parse_partition_by(), self._parse_order()
6949
6950    def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]:
6951        self._match(TokenType.BETWEEN)
6952
6953        return {
6954            "value": (
6955                (self._match_text_seq("UNBOUNDED") and "UNBOUNDED")
6956                or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW")
6957                or self._parse_bitwise()
6958            ),
6959            "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text,
6960        }
6961
6962    def _parse_alias(
6963        self, this: t.Optional[exp.Expression], explicit: bool = False
6964    ) -> t.Optional[exp.Expression]:
6965        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
6966        # so this section tries to parse the clause version and if it fails, it treats the token
6967        # as an identifier (alias)
6968        if self._can_parse_limit_or_offset():
6969            return this
6970
6971        any_token = self._match(TokenType.ALIAS)
6972        comments = self._prev_comments or []
6973
6974        if explicit and not any_token:
6975            return this
6976
6977        if self._match(TokenType.L_PAREN):
6978            aliases = self.expression(
6979                exp.Aliases,
6980                comments=comments,
6981                this=this,
6982                expressions=self._parse_csv(lambda: self._parse_id_var(any_token)),
6983            )
6984            self._match_r_paren(aliases)
6985            return aliases
6986
6987        alias = self._parse_id_var(any_token, tokens=self.ALIAS_TOKENS) or (
6988            self.STRING_ALIASES and self._parse_string_as_identifier()
6989        )
6990
6991        if alias:
6992            comments.extend(alias.pop_comments())
6993            this = self.expression(exp.Alias, comments=comments, this=this, alias=alias)
6994            column = this.this
6995
6996            # Moves the comment next to the alias in `expr /* comment */ AS alias`
6997            if not this.comments and column and column.comments:
6998                this.comments = column.pop_comments()
6999
7000        return this
7001
7002    def _parse_id_var(
7003        self,
7004        any_token: bool = True,
7005        tokens: t.Optional[t.Collection[TokenType]] = None,
7006    ) -> t.Optional[exp.Expression]:
7007        expression = self._parse_identifier()
7008        if not expression and (
7009            (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS)
7010        ):
7011            quoted = self._prev.token_type == TokenType.STRING
7012            expression = self._identifier_expression(quoted=quoted)
7013
7014        return expression
7015
7016    def _parse_string(self) -> t.Optional[exp.Expression]:
7017        if self._match_set(self.STRING_PARSERS):
7018            return self.STRING_PARSERS[self._prev.token_type](self, self._prev)
7019        return self._parse_placeholder()
7020
7021    def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]:
7022        output = exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True)
7023        if output:
7024            output.update_positions(self._prev)
7025        return output
7026
7027    def _parse_number(self) -> t.Optional[exp.Expression]:
7028        if self._match_set(self.NUMERIC_PARSERS):
7029            return self.NUMERIC_PARSERS[self._prev.token_type](self, self._prev)
7030        return self._parse_placeholder()
7031
7032    def _parse_identifier(self) -> t.Optional[exp.Expression]:
7033        if self._match(TokenType.IDENTIFIER):
7034            return self._identifier_expression(quoted=True)
7035        return self._parse_placeholder()
7036
7037    def _parse_var(
7038        self,
7039        any_token: bool = False,
7040        tokens: t.Optional[t.Collection[TokenType]] = None,
7041        upper: bool = False,
7042    ) -> t.Optional[exp.Expression]:
7043        if (
7044            (any_token and self._advance_any())
7045            or self._match(TokenType.VAR)
7046            or (self._match_set(tokens) if tokens else False)
7047        ):
7048            return self.expression(
7049                exp.Var, this=self._prev.text.upper() if upper else self._prev.text
7050            )
7051        return self._parse_placeholder()
7052
7053    def _advance_any(self, ignore_reserved: bool = False) -> t.Optional[Token]:
7054        if self._curr and (ignore_reserved or self._curr.token_type not in self.RESERVED_TOKENS):
7055            self._advance()
7056            return self._prev
7057        return None
7058
7059    def _parse_var_or_string(self, upper: bool = False) -> t.Optional[exp.Expression]:
7060        return self._parse_string() or self._parse_var(any_token=True, upper=upper)
7061
7062    def _parse_primary_or_var(self) -> t.Optional[exp.Expression]:
7063        return self._parse_primary() or self._parse_var(any_token=True)
7064
7065    def _parse_null(self) -> t.Optional[exp.Expression]:
7066        if self._match_set(self.NULL_TOKENS):
7067            return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev)
7068        return self._parse_placeholder()
7069
7070    def _parse_boolean(self) -> t.Optional[exp.Expression]:
7071        if self._match(TokenType.TRUE):
7072            return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev)
7073        if self._match(TokenType.FALSE):
7074            return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev)
7075        return self._parse_placeholder()
7076
7077    def _parse_star(self) -> t.Optional[exp.Expression]:
7078        if self._match(TokenType.STAR):
7079            return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev)
7080        return self._parse_placeholder()
7081
7082    def _parse_parameter(self) -> exp.Parameter:
7083        this = self._parse_identifier() or self._parse_primary_or_var()
7084        return self.expression(exp.Parameter, this=this)
7085
7086    def _parse_placeholder(self) -> t.Optional[exp.Expression]:
7087        if self._match_set(self.PLACEHOLDER_PARSERS):
7088            placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self)
7089            if placeholder:
7090                return placeholder
7091            self._advance(-1)
7092        return None
7093
7094    def _parse_star_op(self, *keywords: str) -> t.Optional[t.List[exp.Expression]]:
7095        if not self._match_texts(keywords):
7096            return None
7097        if self._match(TokenType.L_PAREN, advance=False):
7098            return self._parse_wrapped_csv(self._parse_expression)
7099
7100        expression = self._parse_expression()
7101        return [expression] if expression else None
7102
7103    def _parse_csv(
7104        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA
7105    ) -> t.List[exp.Expression]:
7106        parse_result = parse_method()
7107        items = [parse_result] if parse_result is not None else []
7108
7109        while self._match(sep):
7110            self._add_comments(parse_result)
7111            parse_result = parse_method()
7112            if parse_result is not None:
7113                items.append(parse_result)
7114
7115        return items
7116
7117    def _parse_tokens(
7118        self, parse_method: t.Callable, expressions: t.Dict
7119    ) -> t.Optional[exp.Expression]:
7120        this = parse_method()
7121
7122        while self._match_set(expressions):
7123            this = self.expression(
7124                expressions[self._prev.token_type],
7125                this=this,
7126                comments=self._prev_comments,
7127                expression=parse_method(),
7128            )
7129
7130        return this
7131
7132    def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]:
7133        return self._parse_wrapped_csv(self._parse_id_var, optional=optional)
7134
7135    def _parse_wrapped_csv(
7136        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False
7137    ) -> t.List[exp.Expression]:
7138        return self._parse_wrapped(
7139            lambda: self._parse_csv(parse_method, sep=sep), optional=optional
7140        )
7141
7142    def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any:
7143        wrapped = self._match(TokenType.L_PAREN)
7144        if not wrapped and not optional:
7145            self.raise_error("Expecting (")
7146        parse_result = parse_method()
7147        if wrapped:
7148            self._match_r_paren()
7149        return parse_result
7150
7151    def _parse_expressions(self) -> t.List[exp.Expression]:
7152        return self._parse_csv(self._parse_expression)
7153
7154    def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]:
7155        return self._parse_select() or self._parse_set_operations(
7156            self._parse_alias(self._parse_assignment(), explicit=True)
7157            if alias
7158            else self._parse_assignment()
7159        )
7160
7161    def _parse_ddl_select(self) -> t.Optional[exp.Expression]:
7162        return self._parse_query_modifiers(
7163            self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False))
7164        )
7165
7166    def _parse_transaction(self) -> exp.Transaction | exp.Command:
7167        this = None
7168        if self._match_texts(self.TRANSACTION_KIND):
7169            this = self._prev.text
7170
7171        self._match_texts(("TRANSACTION", "WORK"))
7172
7173        modes = []
7174        while True:
7175            mode = []
7176            while self._match(TokenType.VAR):
7177                mode.append(self._prev.text)
7178
7179            if mode:
7180                modes.append(" ".join(mode))
7181            if not self._match(TokenType.COMMA):
7182                break
7183
7184        return self.expression(exp.Transaction, this=this, modes=modes)
7185
7186    def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback:
7187        chain = None
7188        savepoint = None
7189        is_rollback = self._prev.token_type == TokenType.ROLLBACK
7190
7191        self._match_texts(("TRANSACTION", "WORK"))
7192
7193        if self._match_text_seq("TO"):
7194            self._match_text_seq("SAVEPOINT")
7195            savepoint = self._parse_id_var()
7196
7197        if self._match(TokenType.AND):
7198            chain = not self._match_text_seq("NO")
7199            self._match_text_seq("CHAIN")
7200
7201        if is_rollback:
7202            return self.expression(exp.Rollback, savepoint=savepoint)
7203
7204        return self.expression(exp.Commit, chain=chain)
7205
7206    def _parse_refresh(self) -> exp.Refresh:
7207        self._match(TokenType.TABLE)
7208        return self.expression(exp.Refresh, this=self._parse_string() or self._parse_table())
7209
7210    def _parse_add_column(self) -> t.Optional[exp.Expression]:
7211        if not self._match_text_seq("ADD"):
7212            return None
7213
7214        self._match(TokenType.COLUMN)
7215        exists_column = self._parse_exists(not_=True)
7216        expression = self._parse_field_def()
7217
7218        if expression:
7219            expression.set("exists", exists_column)
7220
7221            # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns
7222            if self._match_texts(("FIRST", "AFTER")):
7223                position = self._prev.text
7224                column_position = self.expression(
7225                    exp.ColumnPosition, this=self._parse_column(), position=position
7226                )
7227                expression.set("position", column_position)
7228
7229        return expression
7230
7231    def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]:
7232        drop = self._match(TokenType.DROP) and self._parse_drop()
7233        if drop and not isinstance(drop, exp.Command):
7234            drop.set("kind", drop.args.get("kind", "COLUMN"))
7235        return drop
7236
7237    # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html
7238    def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition:
7239        return self.expression(
7240            exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists
7241        )
7242
7243    def _parse_alter_table_add(self) -> t.List[exp.Expression]:
7244        index = self._index - 1
7245
7246        if self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False):
7247            return self._parse_csv(
7248                lambda: self.expression(
7249                    exp.AddConstraint, expressions=self._parse_csv(self._parse_constraint)
7250                )
7251            )
7252
7253        self._retreat(index)
7254        if not self.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN and self._match_text_seq("ADD"):
7255            return self._parse_wrapped_csv(self._parse_field_def, optional=True)
7256
7257        if self._match_text_seq("ADD", "COLUMNS"):
7258            schema = self._parse_schema()
7259            if schema:
7260                return [schema]
7261            return []
7262
7263        return self._parse_wrapped_csv(self._parse_add_column, optional=True)
7264
7265    def _parse_alter_table_alter(self) -> t.Optional[exp.Expression]:
7266        if self._match_texts(self.ALTER_ALTER_PARSERS):
7267            return self.ALTER_ALTER_PARSERS[self._prev.text.upper()](self)
7268
7269        # Many dialects support the ALTER [COLUMN] syntax, so if there is no
7270        # keyword after ALTER we default to parsing this statement
7271        self._match(TokenType.COLUMN)
7272        column = self._parse_field(any_token=True)
7273
7274        if self._match_pair(TokenType.DROP, TokenType.DEFAULT):
7275            return self.expression(exp.AlterColumn, this=column, drop=True)
7276        if self._match_pair(TokenType.SET, TokenType.DEFAULT):
7277            return self.expression(exp.AlterColumn, this=column, default=self._parse_assignment())
7278        if self._match(TokenType.COMMENT):
7279            return self.expression(exp.AlterColumn, this=column, comment=self._parse_string())
7280        if self._match_text_seq("DROP", "NOT", "NULL"):
7281            return self.expression(
7282                exp.AlterColumn,
7283                this=column,
7284                drop=True,
7285                allow_null=True,
7286            )
7287        if self._match_text_seq("SET", "NOT", "NULL"):
7288            return self.expression(
7289                exp.AlterColumn,
7290                this=column,
7291                allow_null=False,
7292            )
7293
7294        if self._match_text_seq("SET", "VISIBLE"):
7295            return self.expression(exp.AlterColumn, this=column, visible="VISIBLE")
7296        if self._match_text_seq("SET", "INVISIBLE"):
7297            return self.expression(exp.AlterColumn, this=column, visible="INVISIBLE")
7298
7299        self._match_text_seq("SET", "DATA")
7300        self._match_text_seq("TYPE")
7301        return self.expression(
7302            exp.AlterColumn,
7303            this=column,
7304            dtype=self._parse_types(),
7305            collate=self._match(TokenType.COLLATE) and self._parse_term(),
7306            using=self._match(TokenType.USING) and self._parse_assignment(),
7307        )
7308
7309    def _parse_alter_diststyle(self) -> exp.AlterDistStyle:
7310        if self._match_texts(("ALL", "EVEN", "AUTO")):
7311            return self.expression(exp.AlterDistStyle, this=exp.var(self._prev.text.upper()))
7312
7313        self._match_text_seq("KEY", "DISTKEY")
7314        return self.expression(exp.AlterDistStyle, this=self._parse_column())
7315
7316    def _parse_alter_sortkey(self, compound: t.Optional[bool] = None) -> exp.AlterSortKey:
7317        if compound:
7318            self._match_text_seq("SORTKEY")
7319
7320        if self._match(TokenType.L_PAREN, advance=False):
7321            return self.expression(
7322                exp.AlterSortKey, expressions=self._parse_wrapped_id_vars(), compound=compound
7323            )
7324
7325        self._match_texts(("AUTO", "NONE"))
7326        return self.expression(
7327            exp.AlterSortKey, this=exp.var(self._prev.text.upper()), compound=compound
7328        )
7329
7330    def _parse_alter_table_drop(self) -> t.List[exp.Expression]:
7331        index = self._index - 1
7332
7333        partition_exists = self._parse_exists()
7334        if self._match(TokenType.PARTITION, advance=False):
7335            return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists))
7336
7337        self._retreat(index)
7338        return self._parse_csv(self._parse_drop_column)
7339
7340    def _parse_alter_table_rename(self) -> t.Optional[exp.AlterRename | exp.RenameColumn]:
7341        if self._match(TokenType.COLUMN):
7342            exists = self._parse_exists()
7343            old_column = self._parse_column()
7344            to = self._match_text_seq("TO")
7345            new_column = self._parse_column()
7346
7347            if old_column is None or to is None or new_column is None:
7348                return None
7349
7350            return self.expression(exp.RenameColumn, this=old_column, to=new_column, exists=exists)
7351
7352        self._match_text_seq("TO")
7353        return self.expression(exp.AlterRename, this=self._parse_table(schema=True))
7354
7355    def _parse_alter_table_set(self) -> exp.AlterSet:
7356        alter_set = self.expression(exp.AlterSet)
7357
7358        if self._match(TokenType.L_PAREN, advance=False) or self._match_text_seq(
7359            "TABLE", "PROPERTIES"
7360        ):
7361            alter_set.set("expressions", self._parse_wrapped_csv(self._parse_assignment))
7362        elif self._match_text_seq("FILESTREAM_ON", advance=False):
7363            alter_set.set("expressions", [self._parse_assignment()])
7364        elif self._match_texts(("LOGGED", "UNLOGGED")):
7365            alter_set.set("option", exp.var(self._prev.text.upper()))
7366        elif self._match_text_seq("WITHOUT") and self._match_texts(("CLUSTER", "OIDS")):
7367            alter_set.set("option", exp.var(f"WITHOUT {self._prev.text.upper()}"))
7368        elif self._match_text_seq("LOCATION"):
7369            alter_set.set("location", self._parse_field())
7370        elif self._match_text_seq("ACCESS", "METHOD"):
7371            alter_set.set("access_method", self._parse_field())
7372        elif self._match_text_seq("TABLESPACE"):
7373            alter_set.set("tablespace", self._parse_field())
7374        elif self._match_text_seq("FILE", "FORMAT") or self._match_text_seq("FILEFORMAT"):
7375            alter_set.set("file_format", [self._parse_field()])
7376        elif self._match_text_seq("STAGE_FILE_FORMAT"):
7377            alter_set.set("file_format", self._parse_wrapped_options())
7378        elif self._match_text_seq("STAGE_COPY_OPTIONS"):
7379            alter_set.set("copy_options", self._parse_wrapped_options())
7380        elif self._match_text_seq("TAG") or self._match_text_seq("TAGS"):
7381            alter_set.set("tag", self._parse_csv(self._parse_assignment))
7382        else:
7383            if self._match_text_seq("SERDE"):
7384                alter_set.set("serde", self._parse_field())
7385
7386            alter_set.set("expressions", [self._parse_properties()])
7387
7388        return alter_set
7389
7390    def _parse_alter(self) -> exp.Alter | exp.Command:
7391        start = self._prev
7392
7393        alter_token = self._match_set(self.ALTERABLES) and self._prev
7394        if not alter_token:
7395            return self._parse_as_command(start)
7396
7397        exists = self._parse_exists()
7398        only = self._match_text_seq("ONLY")
7399        this = self._parse_table(schema=True)
7400        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
7401
7402        if self._next:
7403            self._advance()
7404
7405        parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None
7406        if parser:
7407            actions = ensure_list(parser(self))
7408            not_valid = self._match_text_seq("NOT", "VALID")
7409            options = self._parse_csv(self._parse_property)
7410
7411            if not self._curr and actions:
7412                return self.expression(
7413                    exp.Alter,
7414                    this=this,
7415                    kind=alter_token.text.upper(),
7416                    exists=exists,
7417                    actions=actions,
7418                    only=only,
7419                    options=options,
7420                    cluster=cluster,
7421                    not_valid=not_valid,
7422                )
7423
7424        return self._parse_as_command(start)
7425
7426    def _parse_analyze(self) -> exp.Analyze | exp.Command:
7427        start = self._prev
7428        # https://duckdb.org/docs/sql/statements/analyze
7429        if not self._curr:
7430            return self.expression(exp.Analyze)
7431
7432        options = []
7433        while self._match_texts(self.ANALYZE_STYLES):
7434            if self._prev.text.upper() == "BUFFER_USAGE_LIMIT":
7435                options.append(f"BUFFER_USAGE_LIMIT {self._parse_number()}")
7436            else:
7437                options.append(self._prev.text.upper())
7438
7439        this: t.Optional[exp.Expression] = None
7440        inner_expression: t.Optional[exp.Expression] = None
7441
7442        kind = self._curr and self._curr.text.upper()
7443
7444        if self._match(TokenType.TABLE) or self._match(TokenType.INDEX):
7445            this = self._parse_table_parts()
7446        elif self._match_text_seq("TABLES"):
7447            if self._match_set((TokenType.FROM, TokenType.IN)):
7448                kind = f"{kind} {self._prev.text.upper()}"
7449                this = self._parse_table(schema=True, is_db_reference=True)
7450        elif self._match_text_seq("DATABASE"):
7451            this = self._parse_table(schema=True, is_db_reference=True)
7452        elif self._match_text_seq("CLUSTER"):
7453            this = self._parse_table()
7454        # Try matching inner expr keywords before fallback to parse table.
7455        elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7456            kind = None
7457            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7458        else:
7459            # Empty kind  https://prestodb.io/docs/current/sql/analyze.html
7460            kind = None
7461            this = self._parse_table_parts()
7462
7463        partition = self._try_parse(self._parse_partition)
7464        if not partition and self._match_texts(self.PARTITION_KEYWORDS):
7465            return self._parse_as_command(start)
7466
7467        # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7468        if self._match_text_seq("WITH", "SYNC", "MODE") or self._match_text_seq(
7469            "WITH", "ASYNC", "MODE"
7470        ):
7471            mode = f"WITH {self._tokens[self._index - 2].text.upper()} MODE"
7472        else:
7473            mode = None
7474
7475        if self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7476            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7477
7478        properties = self._parse_properties()
7479        return self.expression(
7480            exp.Analyze,
7481            kind=kind,
7482            this=this,
7483            mode=mode,
7484            partition=partition,
7485            properties=properties,
7486            expression=inner_expression,
7487            options=options,
7488        )
7489
7490    # https://spark.apache.org/docs/3.5.1/sql-ref-syntax-aux-analyze-table.html
7491    def _parse_analyze_statistics(self) -> exp.AnalyzeStatistics:
7492        this = None
7493        kind = self._prev.text.upper()
7494        option = self._prev.text.upper() if self._match_text_seq("DELTA") else None
7495        expressions = []
7496
7497        if not self._match_text_seq("STATISTICS"):
7498            self.raise_error("Expecting token STATISTICS")
7499
7500        if self._match_text_seq("NOSCAN"):
7501            this = "NOSCAN"
7502        elif self._match(TokenType.FOR):
7503            if self._match_text_seq("ALL", "COLUMNS"):
7504                this = "FOR ALL COLUMNS"
7505            if self._match_texts("COLUMNS"):
7506                this = "FOR COLUMNS"
7507                expressions = self._parse_csv(self._parse_column_reference)
7508        elif self._match_text_seq("SAMPLE"):
7509            sample = self._parse_number()
7510            expressions = [
7511                self.expression(
7512                    exp.AnalyzeSample,
7513                    sample=sample,
7514                    kind=self._prev.text.upper() if self._match(TokenType.PERCENT) else None,
7515                )
7516            ]
7517
7518        return self.expression(
7519            exp.AnalyzeStatistics, kind=kind, option=option, this=this, expressions=expressions
7520        )
7521
7522    # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ANALYZE.html
7523    def _parse_analyze_validate(self) -> exp.AnalyzeValidate:
7524        kind = None
7525        this = None
7526        expression: t.Optional[exp.Expression] = None
7527        if self._match_text_seq("REF", "UPDATE"):
7528            kind = "REF"
7529            this = "UPDATE"
7530            if self._match_text_seq("SET", "DANGLING", "TO", "NULL"):
7531                this = "UPDATE SET DANGLING TO NULL"
7532        elif self._match_text_seq("STRUCTURE"):
7533            kind = "STRUCTURE"
7534            if self._match_text_seq("CASCADE", "FAST"):
7535                this = "CASCADE FAST"
7536            elif self._match_text_seq("CASCADE", "COMPLETE") and self._match_texts(
7537                ("ONLINE", "OFFLINE")
7538            ):
7539                this = f"CASCADE COMPLETE {self._prev.text.upper()}"
7540                expression = self._parse_into()
7541
7542        return self.expression(exp.AnalyzeValidate, kind=kind, this=this, expression=expression)
7543
7544    def _parse_analyze_columns(self) -> t.Optional[exp.AnalyzeColumns]:
7545        this = self._prev.text.upper()
7546        if self._match_text_seq("COLUMNS"):
7547            return self.expression(exp.AnalyzeColumns, this=f"{this} {self._prev.text.upper()}")
7548        return None
7549
7550    def _parse_analyze_delete(self) -> t.Optional[exp.AnalyzeDelete]:
7551        kind = self._prev.text.upper() if self._match_text_seq("SYSTEM") else None
7552        if self._match_text_seq("STATISTICS"):
7553            return self.expression(exp.AnalyzeDelete, kind=kind)
7554        return None
7555
7556    def _parse_analyze_list(self) -> t.Optional[exp.AnalyzeListChainedRows]:
7557        if self._match_text_seq("CHAINED", "ROWS"):
7558            return self.expression(exp.AnalyzeListChainedRows, expression=self._parse_into())
7559        return None
7560
7561    # https://dev.mysql.com/doc/refman/8.4/en/analyze-table.html
7562    def _parse_analyze_histogram(self) -> exp.AnalyzeHistogram:
7563        this = self._prev.text.upper()
7564        expression: t.Optional[exp.Expression] = None
7565        expressions = []
7566        update_options = None
7567
7568        if self._match_text_seq("HISTOGRAM", "ON"):
7569            expressions = self._parse_csv(self._parse_column_reference)
7570            with_expressions = []
7571            while self._match(TokenType.WITH):
7572                # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7573                if self._match_texts(("SYNC", "ASYNC")):
7574                    if self._match_text_seq("MODE", advance=False):
7575                        with_expressions.append(f"{self._prev.text.upper()} MODE")
7576                        self._advance()
7577                else:
7578                    buckets = self._parse_number()
7579                    if self._match_text_seq("BUCKETS"):
7580                        with_expressions.append(f"{buckets} BUCKETS")
7581            if with_expressions:
7582                expression = self.expression(exp.AnalyzeWith, expressions=with_expressions)
7583
7584            if self._match_texts(("MANUAL", "AUTO")) and self._match(
7585                TokenType.UPDATE, advance=False
7586            ):
7587                update_options = self._prev.text.upper()
7588                self._advance()
7589            elif self._match_text_seq("USING", "DATA"):
7590                expression = self.expression(exp.UsingData, this=self._parse_string())
7591
7592        return self.expression(
7593            exp.AnalyzeHistogram,
7594            this=this,
7595            expressions=expressions,
7596            expression=expression,
7597            update_options=update_options,
7598        )
7599
7600    def _parse_merge(self) -> exp.Merge:
7601        self._match(TokenType.INTO)
7602        target = self._parse_table()
7603
7604        if target and self._match(TokenType.ALIAS, advance=False):
7605            target.set("alias", self._parse_table_alias())
7606
7607        self._match(TokenType.USING)
7608        using = self._parse_table()
7609
7610        self._match(TokenType.ON)
7611        on = self._parse_assignment()
7612
7613        return self.expression(
7614            exp.Merge,
7615            this=target,
7616            using=using,
7617            on=on,
7618            whens=self._parse_when_matched(),
7619            returning=self._parse_returning(),
7620        )
7621
7622    def _parse_when_matched(self) -> exp.Whens:
7623        whens = []
7624
7625        while self._match(TokenType.WHEN):
7626            matched = not self._match(TokenType.NOT)
7627            self._match_text_seq("MATCHED")
7628            source = (
7629                False
7630                if self._match_text_seq("BY", "TARGET")
7631                else self._match_text_seq("BY", "SOURCE")
7632            )
7633            condition = self._parse_assignment() if self._match(TokenType.AND) else None
7634
7635            self._match(TokenType.THEN)
7636
7637            if self._match(TokenType.INSERT):
7638                this = self._parse_star()
7639                if this:
7640                    then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=this)
7641                else:
7642                    then = self.expression(
7643                        exp.Insert,
7644                        this=exp.var("ROW")
7645                        if self._match_text_seq("ROW")
7646                        else self._parse_value(values=False),
7647                        expression=self._match_text_seq("VALUES") and self._parse_value(),
7648                    )
7649            elif self._match(TokenType.UPDATE):
7650                expressions = self._parse_star()
7651                if expressions:
7652                    then = self.expression(exp.Update, expressions=expressions)
7653                else:
7654                    then = self.expression(
7655                        exp.Update,
7656                        expressions=self._match(TokenType.SET)
7657                        and self._parse_csv(self._parse_equality),
7658                    )
7659            elif self._match(TokenType.DELETE):
7660                then = self.expression(exp.Var, this=self._prev.text)
7661            else:
7662                then = self._parse_var_from_options(self.CONFLICT_ACTIONS)
7663
7664            whens.append(
7665                self.expression(
7666                    exp.When,
7667                    matched=matched,
7668                    source=source,
7669                    condition=condition,
7670                    then=then,
7671                )
7672            )
7673        return self.expression(exp.Whens, expressions=whens)
7674
7675    def _parse_show(self) -> t.Optional[exp.Expression]:
7676        parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE)
7677        if parser:
7678            return parser(self)
7679        return self._parse_as_command(self._prev)
7680
7681    def _parse_set_item_assignment(
7682        self, kind: t.Optional[str] = None
7683    ) -> t.Optional[exp.Expression]:
7684        index = self._index
7685
7686        if kind in ("GLOBAL", "SESSION") and self._match_text_seq("TRANSACTION"):
7687            return self._parse_set_transaction(global_=kind == "GLOBAL")
7688
7689        left = self._parse_primary() or self._parse_column()
7690        assignment_delimiter = self._match_texts(("=", "TO"))
7691
7692        if not left or (self.SET_REQUIRES_ASSIGNMENT_DELIMITER and not assignment_delimiter):
7693            self._retreat(index)
7694            return None
7695
7696        right = self._parse_statement() or self._parse_id_var()
7697        if isinstance(right, (exp.Column, exp.Identifier)):
7698            right = exp.var(right.name)
7699
7700        this = self.expression(exp.EQ, this=left, expression=right)
7701        return self.expression(exp.SetItem, this=this, kind=kind)
7702
7703    def _parse_set_transaction(self, global_: bool = False) -> exp.Expression:
7704        self._match_text_seq("TRANSACTION")
7705        characteristics = self._parse_csv(
7706            lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS)
7707        )
7708        return self.expression(
7709            exp.SetItem,
7710            expressions=characteristics,
7711            kind="TRANSACTION",
7712            **{"global": global_},  # type: ignore
7713        )
7714
7715    def _parse_set_item(self) -> t.Optional[exp.Expression]:
7716        parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE)
7717        return parser(self) if parser else self._parse_set_item_assignment(kind=None)
7718
7719    def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command:
7720        index = self._index
7721        set_ = self.expression(
7722            exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag
7723        )
7724
7725        if self._curr:
7726            self._retreat(index)
7727            return self._parse_as_command(self._prev)
7728
7729        return set_
7730
7731    def _parse_var_from_options(
7732        self, options: OPTIONS_TYPE, raise_unmatched: bool = True
7733    ) -> t.Optional[exp.Var]:
7734        start = self._curr
7735        if not start:
7736            return None
7737
7738        option = start.text.upper()
7739        continuations = options.get(option)
7740
7741        index = self._index
7742        self._advance()
7743        for keywords in continuations or []:
7744            if isinstance(keywords, str):
7745                keywords = (keywords,)
7746
7747            if self._match_text_seq(*keywords):
7748                option = f"{option} {' '.join(keywords)}"
7749                break
7750        else:
7751            if continuations or continuations is None:
7752                if raise_unmatched:
7753                    self.raise_error(f"Unknown option {option}")
7754
7755                self._retreat(index)
7756                return None
7757
7758        return exp.var(option)
7759
7760    def _parse_as_command(self, start: Token) -> exp.Command:
7761        while self._curr:
7762            self._advance()
7763        text = self._find_sql(start, self._prev)
7764        size = len(start.text)
7765        self._warn_unsupported()
7766        return exp.Command(this=text[:size], expression=text[size:])
7767
7768    def _parse_dict_property(self, this: str) -> exp.DictProperty:
7769        settings = []
7770
7771        self._match_l_paren()
7772        kind = self._parse_id_var()
7773
7774        if self._match(TokenType.L_PAREN):
7775            while True:
7776                key = self._parse_id_var()
7777                value = self._parse_primary()
7778                if not key and value is None:
7779                    break
7780                settings.append(self.expression(exp.DictSubProperty, this=key, value=value))
7781            self._match(TokenType.R_PAREN)
7782
7783        self._match_r_paren()
7784
7785        return self.expression(
7786            exp.DictProperty,
7787            this=this,
7788            kind=kind.this if kind else None,
7789            settings=settings,
7790        )
7791
7792    def _parse_dict_range(self, this: str) -> exp.DictRange:
7793        self._match_l_paren()
7794        has_min = self._match_text_seq("MIN")
7795        if has_min:
7796            min = self._parse_var() or self._parse_primary()
7797            self._match_text_seq("MAX")
7798            max = self._parse_var() or self._parse_primary()
7799        else:
7800            max = self._parse_var() or self._parse_primary()
7801            min = exp.Literal.number(0)
7802        self._match_r_paren()
7803        return self.expression(exp.DictRange, this=this, min=min, max=max)
7804
7805    def _parse_comprehension(
7806        self, this: t.Optional[exp.Expression]
7807    ) -> t.Optional[exp.Comprehension]:
7808        index = self._index
7809        expression = self._parse_column()
7810        if not self._match(TokenType.IN):
7811            self._retreat(index - 1)
7812            return None
7813        iterator = self._parse_column()
7814        condition = self._parse_assignment() if self._match_text_seq("IF") else None
7815        return self.expression(
7816            exp.Comprehension,
7817            this=this,
7818            expression=expression,
7819            iterator=iterator,
7820            condition=condition,
7821        )
7822
7823    def _parse_heredoc(self) -> t.Optional[exp.Heredoc]:
7824        if self._match(TokenType.HEREDOC_STRING):
7825            return self.expression(exp.Heredoc, this=self._prev.text)
7826
7827        if not self._match_text_seq("$"):
7828            return None
7829
7830        tags = ["$"]
7831        tag_text = None
7832
7833        if self._is_connected():
7834            self._advance()
7835            tags.append(self._prev.text.upper())
7836        else:
7837            self.raise_error("No closing $ found")
7838
7839        if tags[-1] != "$":
7840            if self._is_connected() and self._match_text_seq("$"):
7841                tag_text = tags[-1]
7842                tags.append("$")
7843            else:
7844                self.raise_error("No closing $ found")
7845
7846        heredoc_start = self._curr
7847
7848        while self._curr:
7849            if self._match_text_seq(*tags, advance=False):
7850                this = self._find_sql(heredoc_start, self._prev)
7851                self._advance(len(tags))
7852                return self.expression(exp.Heredoc, this=this, tag=tag_text)
7853
7854            self._advance()
7855
7856        self.raise_error(f"No closing {''.join(tags)} found")
7857        return None
7858
7859    def _find_parser(
7860        self, parsers: t.Dict[str, t.Callable], trie: t.Dict
7861    ) -> t.Optional[t.Callable]:
7862        if not self._curr:
7863            return None
7864
7865        index = self._index
7866        this = []
7867        while True:
7868            # The current token might be multiple words
7869            curr = self._curr.text.upper()
7870            key = curr.split(" ")
7871            this.append(curr)
7872
7873            self._advance()
7874            result, trie = in_trie(trie, key)
7875            if result == TrieResult.FAILED:
7876                break
7877
7878            if result == TrieResult.EXISTS:
7879                subparser = parsers[" ".join(this)]
7880                return subparser
7881
7882        self._retreat(index)
7883        return None
7884
7885    def _match(self, token_type, advance=True, expression=None):
7886        if not self._curr:
7887            return None
7888
7889        if self._curr.token_type == token_type:
7890            if advance:
7891                self._advance()
7892            self._add_comments(expression)
7893            return True
7894
7895        return None
7896
7897    def _match_set(self, types, advance=True):
7898        if not self._curr:
7899            return None
7900
7901        if self._curr.token_type in types:
7902            if advance:
7903                self._advance()
7904            return True
7905
7906        return None
7907
7908    def _match_pair(self, token_type_a, token_type_b, advance=True):
7909        if not self._curr or not self._next:
7910            return None
7911
7912        if self._curr.token_type == token_type_a and self._next.token_type == token_type_b:
7913            if advance:
7914                self._advance(2)
7915            return True
7916
7917        return None
7918
7919    def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
7920        if not self._match(TokenType.L_PAREN, expression=expression):
7921            self.raise_error("Expecting (")
7922
7923    def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
7924        if not self._match(TokenType.R_PAREN, expression=expression):
7925            self.raise_error("Expecting )")
7926
7927    def _match_texts(self, texts, advance=True):
7928        if (
7929            self._curr
7930            and self._curr.token_type != TokenType.STRING
7931            and self._curr.text.upper() in texts
7932        ):
7933            if advance:
7934                self._advance()
7935            return True
7936        return None
7937
7938    def _match_text_seq(self, *texts, advance=True):
7939        index = self._index
7940        for text in texts:
7941            if (
7942                self._curr
7943                and self._curr.token_type != TokenType.STRING
7944                and self._curr.text.upper() == text
7945            ):
7946                self._advance()
7947            else:
7948                self._retreat(index)
7949                return None
7950
7951        if not advance:
7952            self._retreat(index)
7953
7954        return True
7955
7956    def _replace_lambda(
7957        self, node: t.Optional[exp.Expression], expressions: t.List[exp.Expression]
7958    ) -> t.Optional[exp.Expression]:
7959        if not node:
7960            return node
7961
7962        lambda_types = {e.name: e.args.get("to") or False for e in expressions}
7963
7964        for column in node.find_all(exp.Column):
7965            typ = lambda_types.get(column.parts[0].name)
7966            if typ is not None:
7967                dot_or_id = column.to_dot() if column.table else column.this
7968
7969                if typ:
7970                    dot_or_id = self.expression(
7971                        exp.Cast,
7972                        this=dot_or_id,
7973                        to=typ,
7974                    )
7975
7976                parent = column.parent
7977
7978                while isinstance(parent, exp.Dot):
7979                    if not isinstance(parent.parent, exp.Dot):
7980                        parent.replace(dot_or_id)
7981                        break
7982                    parent = parent.parent
7983                else:
7984                    if column is node:
7985                        node = dot_or_id
7986                    else:
7987                        column.replace(dot_or_id)
7988        return node
7989
7990    def _parse_truncate_table(self) -> t.Optional[exp.TruncateTable] | exp.Expression:
7991        start = self._prev
7992
7993        # Not to be confused with TRUNCATE(number, decimals) function call
7994        if self._match(TokenType.L_PAREN):
7995            self._retreat(self._index - 2)
7996            return self._parse_function()
7997
7998        # Clickhouse supports TRUNCATE DATABASE as well
7999        is_database = self._match(TokenType.DATABASE)
8000
8001        self._match(TokenType.TABLE)
8002
8003        exists = self._parse_exists(not_=False)
8004
8005        expressions = self._parse_csv(
8006            lambda: self._parse_table(schema=True, is_db_reference=is_database)
8007        )
8008
8009        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
8010
8011        if self._match_text_seq("RESTART", "IDENTITY"):
8012            identity = "RESTART"
8013        elif self._match_text_seq("CONTINUE", "IDENTITY"):
8014            identity = "CONTINUE"
8015        else:
8016            identity = None
8017
8018        if self._match_text_seq("CASCADE") or self._match_text_seq("RESTRICT"):
8019            option = self._prev.text
8020        else:
8021            option = None
8022
8023        partition = self._parse_partition()
8024
8025        # Fallback case
8026        if self._curr:
8027            return self._parse_as_command(start)
8028
8029        return self.expression(
8030            exp.TruncateTable,
8031            expressions=expressions,
8032            is_database=is_database,
8033            exists=exists,
8034            cluster=cluster,
8035            identity=identity,
8036            option=option,
8037            partition=partition,
8038        )
8039
8040    def _parse_with_operator(self) -> t.Optional[exp.Expression]:
8041        this = self._parse_ordered(self._parse_opclass)
8042
8043        if not self._match(TokenType.WITH):
8044            return this
8045
8046        op = self._parse_var(any_token=True)
8047
8048        return self.expression(exp.WithOperator, this=this, op=op)
8049
8050    def _parse_wrapped_options(self) -> t.List[t.Optional[exp.Expression]]:
8051        self._match(TokenType.EQ)
8052        self._match(TokenType.L_PAREN)
8053
8054        opts: t.List[t.Optional[exp.Expression]] = []
8055        option: exp.Expression | None
8056        while self._curr and not self._match(TokenType.R_PAREN):
8057            if self._match_text_seq("FORMAT_NAME", "="):
8058                # The FORMAT_NAME can be set to an identifier for Snowflake and T-SQL
8059                option = self._parse_format_name()
8060            else:
8061                option = self._parse_property()
8062
8063            if option is None:
8064                self.raise_error("Unable to parse option")
8065                break
8066
8067            opts.append(option)
8068
8069        return opts
8070
8071    def _parse_copy_parameters(self) -> t.List[exp.CopyParameter]:
8072        sep = TokenType.COMMA if self.dialect.COPY_PARAMS_ARE_CSV else None
8073
8074        options = []
8075        while self._curr and not self._match(TokenType.R_PAREN, advance=False):
8076            option = self._parse_var(any_token=True)
8077            prev = self._prev.text.upper()
8078
8079            # Different dialects might separate options and values by white space, "=" and "AS"
8080            self._match(TokenType.EQ)
8081            self._match(TokenType.ALIAS)
8082
8083            param = self.expression(exp.CopyParameter, this=option)
8084
8085            if prev in self.COPY_INTO_VARLEN_OPTIONS and self._match(
8086                TokenType.L_PAREN, advance=False
8087            ):
8088                # Snowflake FILE_FORMAT case, Databricks COPY & FORMAT options
8089                param.set("expressions", self._parse_wrapped_options())
8090            elif prev == "FILE_FORMAT":
8091                # T-SQL's external file format case
8092                param.set("expression", self._parse_field())
8093            else:
8094                param.set("expression", self._parse_unquoted_field())
8095
8096            options.append(param)
8097            self._match(sep)
8098
8099        return options
8100
8101    def _parse_credentials(self) -> t.Optional[exp.Credentials]:
8102        expr = self.expression(exp.Credentials)
8103
8104        if self._match_text_seq("STORAGE_INTEGRATION", "="):
8105            expr.set("storage", self._parse_field())
8106        if self._match_text_seq("CREDENTIALS"):
8107            # Snowflake case: CREDENTIALS = (...), Redshift case: CREDENTIALS <string>
8108            creds = (
8109                self._parse_wrapped_options() if self._match(TokenType.EQ) else self._parse_field()
8110            )
8111            expr.set("credentials", creds)
8112        if self._match_text_seq("ENCRYPTION"):
8113            expr.set("encryption", self._parse_wrapped_options())
8114        if self._match_text_seq("IAM_ROLE"):
8115            expr.set("iam_role", self._parse_field())
8116        if self._match_text_seq("REGION"):
8117            expr.set("region", self._parse_field())
8118
8119        return expr
8120
8121    def _parse_file_location(self) -> t.Optional[exp.Expression]:
8122        return self._parse_field()
8123
8124    def _parse_copy(self) -> exp.Copy | exp.Command:
8125        start = self._prev
8126
8127        self._match(TokenType.INTO)
8128
8129        this = (
8130            self._parse_select(nested=True, parse_subquery_alias=False)
8131            if self._match(TokenType.L_PAREN, advance=False)
8132            else self._parse_table(schema=True)
8133        )
8134
8135        kind = self._match(TokenType.FROM) or not self._match_text_seq("TO")
8136
8137        files = self._parse_csv(self._parse_file_location)
8138        credentials = self._parse_credentials()
8139
8140        self._match_text_seq("WITH")
8141
8142        params = self._parse_wrapped(self._parse_copy_parameters, optional=True)
8143
8144        # Fallback case
8145        if self._curr:
8146            return self._parse_as_command(start)
8147
8148        return self.expression(
8149            exp.Copy,
8150            this=this,
8151            kind=kind,
8152            credentials=credentials,
8153            files=files,
8154            params=params,
8155        )
8156
8157    def _parse_normalize(self) -> exp.Normalize:
8158        return self.expression(
8159            exp.Normalize,
8160            this=self._parse_bitwise(),
8161            form=self._match(TokenType.COMMA) and self._parse_var(),
8162        )
8163
8164    def _parse_ceil_floor(self, expr_type: t.Type[TCeilFloor]) -> TCeilFloor:
8165        args = self._parse_csv(lambda: self._parse_lambda())
8166
8167        this = seq_get(args, 0)
8168        decimals = seq_get(args, 1)
8169
8170        return expr_type(
8171            this=this, decimals=decimals, to=self._match_text_seq("TO") and self._parse_var()
8172        )
8173
8174    def _parse_star_ops(self) -> t.Optional[exp.Expression]:
8175        if self._match_text_seq("COLUMNS", "(", advance=False):
8176            this = self._parse_function()
8177            if isinstance(this, exp.Columns):
8178                this.set("unpack", True)
8179            return this
8180
8181        return self.expression(
8182            exp.Star,
8183            **{  # type: ignore
8184                "except": self._parse_star_op("EXCEPT", "EXCLUDE"),
8185                "replace": self._parse_star_op("REPLACE"),
8186                "rename": self._parse_star_op("RENAME"),
8187            },
8188        )
8189
8190    def _parse_grant_privilege(self) -> t.Optional[exp.GrantPrivilege]:
8191        privilege_parts = []
8192
8193        # Keep consuming consecutive keywords until comma (end of this privilege) or ON
8194        # (end of privilege list) or L_PAREN (start of column list) are met
8195        while self._curr and not self._match_set(self.PRIVILEGE_FOLLOW_TOKENS, advance=False):
8196            privilege_parts.append(self._curr.text.upper())
8197            self._advance()
8198
8199        this = exp.var(" ".join(privilege_parts))
8200        expressions = (
8201            self._parse_wrapped_csv(self._parse_column)
8202            if self._match(TokenType.L_PAREN, advance=False)
8203            else None
8204        )
8205
8206        return self.expression(exp.GrantPrivilege, this=this, expressions=expressions)
8207
8208    def _parse_grant_principal(self) -> t.Optional[exp.GrantPrincipal]:
8209        kind = self._match_texts(("ROLE", "GROUP")) and self._prev.text.upper()
8210        principal = self._parse_id_var()
8211
8212        if not principal:
8213            return None
8214
8215        return self.expression(exp.GrantPrincipal, this=principal, kind=kind)
8216
8217    def _parse_grant(self) -> exp.Grant | exp.Command:
8218        start = self._prev
8219
8220        privileges = self._parse_csv(self._parse_grant_privilege)
8221
8222        self._match(TokenType.ON)
8223        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
8224
8225        # Attempt to parse the securable e.g. MySQL allows names
8226        # such as "foo.*", "*.*" which are not easily parseable yet
8227        securable = self._try_parse(self._parse_table_parts)
8228
8229        if not securable or not self._match_text_seq("TO"):
8230            return self._parse_as_command(start)
8231
8232        principals = self._parse_csv(self._parse_grant_principal)
8233
8234        grant_option = self._match_text_seq("WITH", "GRANT", "OPTION")
8235
8236        if self._curr:
8237            return self._parse_as_command(start)
8238
8239        return self.expression(
8240            exp.Grant,
8241            privileges=privileges,
8242            kind=kind,
8243            securable=securable,
8244            principals=principals,
8245            grant_option=grant_option,
8246        )
8247
8248    def _parse_overlay(self) -> exp.Overlay:
8249        return self.expression(
8250            exp.Overlay,
8251            **{  # type: ignore
8252                "this": self._parse_bitwise(),
8253                "expression": self._match_text_seq("PLACING") and self._parse_bitwise(),
8254                "from": self._match_text_seq("FROM") and self._parse_bitwise(),
8255                "for": self._match_text_seq("FOR") and self._parse_bitwise(),
8256            },
8257        )
8258
8259    def _parse_format_name(self) -> exp.Property:
8260        # Note: Although not specified in the docs, Snowflake does accept a string/identifier
8261        # for FILE_FORMAT = <format_name>
8262        return self.expression(
8263            exp.Property,
8264            this=exp.var("FORMAT_NAME"),
8265            value=self._parse_string() or self._parse_table_parts(),
8266        )
8267
8268    def _parse_max_min_by(self, expr_type: t.Type[exp.AggFunc]) -> exp.AggFunc:
8269        args: t.List[exp.Expression] = []
8270
8271        if self._match(TokenType.DISTINCT):
8272            args.append(self.expression(exp.Distinct, expressions=[self._parse_assignment()]))
8273            self._match(TokenType.COMMA)
8274
8275        args.extend(self._parse_csv(self._parse_assignment))
8276
8277        return self.expression(
8278            expr_type, this=seq_get(args, 0), expression=seq_get(args, 1), count=seq_get(args, 2)
8279        )
8280
8281    def _identifier_expression(
8282        self, token: t.Optional[Token] = None, **kwargs: t.Any
8283    ) -> exp.Identifier:
8284        token = token or self._prev
8285        expression = self.expression(exp.Identifier, this=token.text, **kwargs)
8286        expression.update_positions(token)
8287        return expression

Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.

Arguments:
  • error_level: The desired error level. Default: ErrorLevel.IMMEDIATE
  • error_message_context: The amount of context to capture from a query string when displaying the error message (in number of characters). Default: 100
  • max_errors: Maximum number of error messages to include in a raised ParseError. This is only relevant if error_level is ErrorLevel.RAISE. Default: 3
Parser( error_level: Optional[sqlglot.errors.ErrorLevel] = None, error_message_context: int = 100, max_errors: int = 3, dialect: Union[str, sqlglot.dialects.Dialect, Type[sqlglot.dialects.Dialect], NoneType] = None)
1520    def __init__(
1521        self,
1522        error_level: t.Optional[ErrorLevel] = None,
1523        error_message_context: int = 100,
1524        max_errors: int = 3,
1525        dialect: DialectType = None,
1526    ):
1527        from sqlglot.dialects import Dialect
1528
1529        self.error_level = error_level or ErrorLevel.IMMEDIATE
1530        self.error_message_context = error_message_context
1531        self.max_errors = max_errors
1532        self.dialect = Dialect.get_or_raise(dialect)
1533        self.reset()
FUNCTIONS: Dict[str, Callable] = {'ABS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Abs'>>, 'ADD_MONTHS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.AddMonths'>>, 'AND': <bound method Func.from_arg_list of <class 'sqlglot.expressions.And'>>, 'ANONYMOUS_AGG_FUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.AnonymousAggFunc'>>, 'ANY_VALUE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.AnyValue'>>, 'APPLY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Apply'>>, 'APPROX_DISTINCT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ApproxDistinct'>>, 'APPROX_COUNT_DISTINCT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ApproxDistinct'>>, 'APPROX_QUANTILE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ApproxQuantile'>>, 'APPROX_TOP_K': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ApproxTopK'>>, 'ARG_MAX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMax'>>, 'ARGMAX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMax'>>, 'MAX_BY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMax'>>, 'ARG_MIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMin'>>, 'ARGMIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMin'>>, 'MIN_BY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArgMin'>>, 'ARRAY': <function Parser.<lambda>>, 'ARRAY_AGG': <function Parser.<lambda>>, 'ARRAY_ALL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayAll'>>, 'ARRAY_ANY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayAny'>>, 'ARRAY_CONCAT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayConcat'>>, 'ARRAY_CAT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayConcat'>>, 'ARRAY_CONSTRUCT_COMPACT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayConstructCompact'>>, 'ARRAY_CONTAINS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayContains'>>, 'ARRAY_HAS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayContains'>>, 'ARRAY_CONTAINS_ALL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayContainsAll'>>, 'ARRAY_HAS_ALL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayContainsAll'>>, 'FILTER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayFilter'>>, 'ARRAY_FILTER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayFilter'>>, 'ARRAY_OVERLAPS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayOverlaps'>>, 'ARRAY_SIZE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArraySize'>>, 'ARRAY_LENGTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArraySize'>>, 'ARRAY_SORT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArraySort'>>, 'ARRAY_SUM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArraySum'>>, 'ARRAY_TO_STRING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayToString'>>, 'ARRAY_JOIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayToString'>>, 'ARRAY_UNION_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayUnionAgg'>>, 'ARRAY_UNIQUE_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayUniqueAgg'>>, 'AVG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Avg'>>, 'CASE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Case'>>, 'CAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Cast'>>, 'CAST_TO_STR_TYPE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CastToStrType'>>, 'CBRT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Cbrt'>>, 'CEIL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Ceil'>>, 'CEILING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Ceil'>>, 'CHR': <function Parser.<lambda>>, 'CHAR': <function Parser.<lambda>>, 'COALESCE': <function build_coalesce>, 'IFNULL': <function build_coalesce>, 'NVL': <function build_coalesce>, 'COLLATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Collate'>>, 'COLUMNS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Columns'>>, 'COMBINED_AGG_FUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CombinedAggFunc'>>, 'COMBINED_PARAMETERIZED_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CombinedParameterizedAgg'>>, 'CONCAT': <function Parser.<lambda>>, 'CONCAT_WS': <function Parser.<lambda>>, 'CONNECT_BY_ROOT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ConnectByRoot'>>, 'CONTAINS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Contains'>>, 'CONVERT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Convert'>>, 'CONVERT_TIMEZONE': <function build_convert_timezone>, 'CONVERT_TO_CHARSET': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ConvertToCharset'>>, 'CORR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Corr'>>, 'COUNT': <function Parser.<lambda>>, 'COUNT_IF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CountIf'>>, 'COUNTIF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CountIf'>>, 'COVAR_POP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CovarPop'>>, 'COVAR_SAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CovarSamp'>>, 'CURRENT_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentDate'>>, 'CURRENT_DATETIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentDatetime'>>, 'CURRENT_SCHEMA': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentSchema'>>, 'CURRENT_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentTime'>>, 'CURRENT_TIMESTAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentTimestamp'>>, 'CURRENT_USER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentUser'>>, 'DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Date'>>, 'DATE_ADD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateAdd'>>, 'DATE_BIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateBin'>>, 'DATEDIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateDiff'>>, 'DATE_DIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateDiff'>>, 'DATE_FROM_PARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateFromParts'>>, 'DATEFROMPARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateFromParts'>>, 'DATE_STR_TO_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateStrToDate'>>, 'DATE_SUB': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateSub'>>, 'DATE_TO_DATE_STR': <function Parser.<lambda>>, 'DATE_TO_DI': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateToDi'>>, 'DATE_TRUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DateTrunc'>>, 'DATETIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Datetime'>>, 'DATETIME_ADD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DatetimeAdd'>>, 'DATETIME_DIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DatetimeDiff'>>, 'DATETIME_SUB': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DatetimeSub'>>, 'DATETIME_TRUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DatetimeTrunc'>>, 'DAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Day'>>, 'DAY_OF_MONTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfMonth'>>, 'DAYOFMONTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfMonth'>>, 'DAY_OF_WEEK': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfWeek'>>, 'DAYOFWEEK': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfWeek'>>, 'DAYOFWEEK_ISO': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfWeekIso'>>, 'ISODOW': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfWeekIso'>>, 'DAY_OF_YEAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfYear'>>, 'DAYOFYEAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DayOfYear'>>, 'DECODE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Decode'>>, 'DI_TO_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.DiToDate'>>, 'ENCODE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Encode'>>, 'EXISTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Exists'>>, 'EXP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Exp'>>, 'EXPLODE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Explode'>>, 'EXPLODE_OUTER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ExplodeOuter'>>, 'EXPLODING_GENERATE_SERIES': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ExplodingGenerateSeries'>>, 'EXTRACT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Extract'>>, 'FEATURES_AT_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.FeaturesAtTime'>>, 'FIRST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.First'>>, 'FIRST_VALUE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.FirstValue'>>, 'FLATTEN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Flatten'>>, 'FLOOR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Floor'>>, 'FROM_BASE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.FromBase'>>, 'FROM_BASE64': <bound method Func.from_arg_list of <class 'sqlglot.expressions.FromBase64'>>, 'FROM_ISO8601_TIMESTAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.FromISO8601Timestamp'>>, 'GAP_FILL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.GapFill'>>, 'GENERATE_DATE_ARRAY': <function Parser.<lambda>>, 'GENERATE_SERIES': <bound method Func.from_arg_list of <class 'sqlglot.expressions.GenerateSeries'>>, 'GENERATE_TIMESTAMP_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.GenerateTimestampArray'>>, 'GREATEST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Greatest'>>, 'GROUP_CONCAT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.GroupConcat'>>, 'HEX': <function build_hex>, 'HLL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Hll'>>, 'IF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.If'>>, 'IIF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.If'>>, 'INITCAP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Initcap'>>, 'INLINE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Inline'>>, 'INT64': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Int64'>>, 'IS_ASCII': <bound method Func.from_arg_list of <class 'sqlglot.expressions.IsAscii'>>, 'IS_INF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.IsInf'>>, 'ISINF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.IsInf'>>, 'IS_NAN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.IsNan'>>, 'ISNAN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.IsNan'>>, 'J_S_O_N_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONArray'>>, 'J_S_O_N_ARRAY_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONArrayAgg'>>, 'JSON_ARRAY_CONTAINS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONArrayContains'>>, 'JSONB_CONTAINS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONBContains'>>, 'JSONB_EXISTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONBExists'>>, 'JSONB_EXTRACT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONBExtract'>>, 'JSONB_EXTRACT_SCALAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONBExtractScalar'>>, 'J_S_O_N_B_OBJECT_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONBObjectAgg'>>, 'J_S_O_N_CAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONCast'>>, 'J_S_O_N_EXISTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONExists'>>, 'JSON_EXTRACT': <function build_extract_json_with_path.<locals>._builder>, 'JSON_EXTRACT_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONExtractArray'>>, 'JSON_EXTRACT_SCALAR': <function build_extract_json_with_path.<locals>._builder>, 'JSON_FORMAT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONFormat'>>, 'J_S_O_N_OBJECT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONObject'>>, 'J_S_O_N_OBJECT_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONObjectAgg'>>, 'J_S_O_N_TABLE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONTable'>>, 'J_S_O_N_VALUE_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.JSONValueArray'>>, 'LAG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Lag'>>, 'LAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Last'>>, 'LAST_DAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LastDay'>>, 'LAST_DAY_OF_MONTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LastDay'>>, 'LAST_VALUE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LastValue'>>, 'LEAD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Lead'>>, 'LEAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Least'>>, 'LEFT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Left'>>, 'LENGTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Length'>>, 'LEN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Length'>>, 'CHAR_LENGTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Length'>>, 'CHARACTER_LENGTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Length'>>, 'LEVENSHTEIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Levenshtein'>>, 'LIST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.List'>>, 'LN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Ln'>>, 'LOG': <function build_logarithm>, 'LOGICAL_AND': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalAnd'>>, 'BOOL_AND': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalAnd'>>, 'BOOLAND_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalAnd'>>, 'LOGICAL_OR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalOr'>>, 'BOOL_OR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalOr'>>, 'BOOLOR_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LogicalOr'>>, 'LOWER': <function build_lower>, 'LCASE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Lower'>>, 'LOWER_HEX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.LowerHex'>>, 'MD5': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MD5'>>, 'MD5_DIGEST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MD5Digest'>>, 'MAKE_INTERVAL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MakeInterval'>>, 'MAP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Map'>>, 'MAP_FROM_ENTRIES': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MapFromEntries'>>, 'MATCH_AGAINST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MatchAgainst'>>, 'MAX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Max'>>, 'MEDIAN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Median'>>, 'MIN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Min'>>, 'MONTH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Month'>>, 'MONTHS_BETWEEN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.MonthsBetween'>>, 'NEXT_VALUE_FOR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.NextValueFor'>>, 'NORMALIZE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Normalize'>>, 'NTH_VALUE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.NthValue'>>, 'NULLIF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Nullif'>>, 'NUMBER_TO_STR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.NumberToStr'>>, 'NVL2': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Nvl2'>>, 'OBJECT_INSERT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ObjectInsert'>>, 'OPEN_J_S_O_N': <bound method Func.from_arg_list of <class 'sqlglot.expressions.OpenJSON'>>, 'OR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Or'>>, 'OVERLAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Overlay'>>, 'PAD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Pad'>>, 'PARAMETERIZED_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ParameterizedAgg'>>, 'PARSE_JSON': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ParseJSON'>>, 'JSON_PARSE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ParseJSON'>>, 'PERCENTILE_CONT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.PercentileCont'>>, 'PERCENTILE_DISC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.PercentileDisc'>>, 'POSEXPLODE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Posexplode'>>, 'POSEXPLODE_OUTER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.PosexplodeOuter'>>, 'POWER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Pow'>>, 'POW': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Pow'>>, 'PREDICT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Predict'>>, 'QUANTILE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Quantile'>>, 'QUARTER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Quarter'>>, 'RAND': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Rand'>>, 'RANDOM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Rand'>>, 'RANDN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Randn'>>, 'RANGE_N': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RangeN'>>, 'READ_CSV': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ReadCSV'>>, 'REDUCE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Reduce'>>, 'REGEXP_EXTRACT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpExtract'>>, 'REGEXP_EXTRACT_ALL': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpExtractAll'>>, 'REGEXP_I_LIKE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpILike'>>, 'REGEXP_LIKE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpLike'>>, 'REGEXP_REPLACE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpReplace'>>, 'REGEXP_SPLIT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RegexpSplit'>>, 'REPEAT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Repeat'>>, 'RIGHT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Right'>>, 'ROUND': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Round'>>, 'ROW_NUMBER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.RowNumber'>>, 'SHA': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SHA'>>, 'SHA1': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SHA'>>, 'SHA2': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SHA2'>>, 'SAFE_DIVIDE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SafeDivide'>>, 'SIGN': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Sign'>>, 'SIGNUM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Sign'>>, 'SORT_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SortArray'>>, 'SPLIT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Split'>>, 'SPLIT_PART': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SplitPart'>>, 'SQRT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Sqrt'>>, 'STANDARD_HASH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StandardHash'>>, 'STAR_MAP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StarMap'>>, 'STARTS_WITH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StartsWith'>>, 'STARTSWITH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StartsWith'>>, 'STDDEV': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Stddev'>>, 'STDEV': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Stddev'>>, 'STDDEV_POP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StddevPop'>>, 'STDDEV_SAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StddevSamp'>>, 'STR_POSITION': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrPosition'>>, 'STR_TO_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrToDate'>>, 'STR_TO_MAP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrToMap'>>, 'STR_TO_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrToTime'>>, 'STR_TO_UNIX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrToUnix'>>, 'STRING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.String'>>, 'STRING_TO_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StringToArray'>>, 'SPLIT_BY_STRING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StringToArray'>>, 'STRUCT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Struct'>>, 'STRUCT_EXTRACT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StructExtract'>>, 'STUFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Stuff'>>, 'INSERT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Stuff'>>, 'SUBSTRING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Substring'>>, 'SUBSTR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Substring'>>, 'SUM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Sum'>>, 'TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Time'>>, 'TIME_ADD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeAdd'>>, 'TIME_DIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeDiff'>>, 'TIME_FROM_PARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeFromParts'>>, 'TIMEFROMPARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeFromParts'>>, 'TIME_STR_TO_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeStrToDate'>>, 'TIME_STR_TO_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeStrToTime'>>, 'TIME_STR_TO_UNIX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeStrToUnix'>>, 'TIME_SUB': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeSub'>>, 'TIME_TO_STR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeToStr'>>, 'TIME_TO_TIME_STR': <function Parser.<lambda>>, 'TIME_TO_UNIX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeToUnix'>>, 'TIME_TRUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimeTrunc'>>, 'TIMESTAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Timestamp'>>, 'TIMESTAMP_ADD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampAdd'>>, 'TIMESTAMPDIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampDiff'>>, 'TIMESTAMP_DIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampDiff'>>, 'TIMESTAMP_FROM_PARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampFromParts'>>, 'TIMESTAMPFROMPARTS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampFromParts'>>, 'TIMESTAMP_SUB': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampSub'>>, 'TIMESTAMP_TRUNC': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TimestampTrunc'>>, 'TO_ARRAY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToArray'>>, 'TO_BASE64': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToBase64'>>, 'TO_CHAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToChar'>>, 'TO_DAYS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToDays'>>, 'TO_DOUBLE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToDouble'>>, 'TO_MAP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToMap'>>, 'TO_NUMBER': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ToNumber'>>, 'TRANSFORM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Transform'>>, 'TRIM': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Trim'>>, 'TRY': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Try'>>, 'TRY_CAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TryCast'>>, 'TS_OR_DI_TO_DI': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDiToDi'>>, 'TS_OR_DS_ADD': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsAdd'>>, 'TS_OR_DS_DIFF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsDiff'>>, 'TS_OR_DS_TO_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsToDate'>>, 'TS_OR_DS_TO_DATE_STR': <function Parser.<lambda>>, 'TS_OR_DS_TO_DATETIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsToDatetime'>>, 'TS_OR_DS_TO_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsToTime'>>, 'TS_OR_DS_TO_TIMESTAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.TsOrDsToTimestamp'>>, 'UNHEX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Unhex'>>, 'UNICODE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Unicode'>>, 'UNIX_DATE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.UnixDate'>>, 'UNIX_SECONDS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.UnixSeconds'>>, 'UNIX_TO_STR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.UnixToStr'>>, 'UNIX_TO_TIME': <bound method Func.from_arg_list of <class 'sqlglot.expressions.UnixToTime'>>, 'UNIX_TO_TIME_STR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.UnixToTimeStr'>>, 'UNNEST': <function Parser.<lambda>>, 'UPPER': <function build_upper>, 'UCASE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Upper'>>, 'UUID': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Uuid'>>, 'GEN_RANDOM_UUID': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Uuid'>>, 'GENERATE_UUID': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Uuid'>>, 'UUID_STRING': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Uuid'>>, 'VAR_MAP': <function build_var_map>, 'VARIANCE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Variance'>>, 'VARIANCE_SAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Variance'>>, 'VAR_SAMP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Variance'>>, 'VARIANCE_POP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.VariancePop'>>, 'VAR_POP': <bound method Func.from_arg_list of <class 'sqlglot.expressions.VariancePop'>>, 'WEEK': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Week'>>, 'WEEK_OF_YEAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.WeekOfYear'>>, 'WEEKOFYEAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.WeekOfYear'>>, 'XMLELEMENT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.XMLElement'>>, 'X_M_L_TABLE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.XMLTable'>>, 'XOR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Xor'>>, 'YEAR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Year'>>, 'ARRAYAGG': <function Parser.<lambda>>, 'GLOB': <function Parser.<lambda>>, 'JSON_EXTRACT_PATH_TEXT': <function build_extract_json_with_path.<locals>._builder>, 'LIKE': <function build_like>, 'LOG2': <function Parser.<lambda>>, 'LOG10': <function Parser.<lambda>>, 'LPAD': <function Parser.<lambda>>, 'LEFTPAD': <function Parser.<lambda>>, 'LTRIM': <function Parser.<lambda>>, 'MOD': <function build_mod>, 'RIGHTPAD': <function Parser.<lambda>>, 'RPAD': <function Parser.<lambda>>, 'RTRIM': <function Parser.<lambda>>, 'SCOPE_RESOLUTION': <function Parser.<lambda>>, 'STRPOS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrPosition'>>, 'CHARINDEX': <function Parser.<lambda>>, 'INSTR': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StrPosition'>>, 'LOCATE': <function Parser.<lambda>>, 'TO_HEX': <function build_hex>}
NO_PAREN_FUNCTIONS = {<TokenType.CURRENT_DATE: 'CURRENT_DATE'>: <class 'sqlglot.expressions.CurrentDate'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>: <class 'sqlglot.expressions.CurrentDate'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>: <class 'sqlglot.expressions.CurrentTime'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>: <class 'sqlglot.expressions.CurrentTimestamp'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>: <class 'sqlglot.expressions.CurrentUser'>}
STRUCT_TYPE_TOKENS = {<TokenType.UNION: 'UNION'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.NESTED: 'NESTED'>, <TokenType.OBJECT: 'OBJECT'>}
NESTED_TYPE_TOKENS = {<TokenType.ARRAY: 'ARRAY'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.LIST: 'LIST'>, <TokenType.UNION: 'UNION'>, <TokenType.NESTED: 'NESTED'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MAP: 'MAP'>}
ENUM_TYPE_TOKENS = {<TokenType.ENUM16: 'ENUM16'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.ENUM: 'ENUM'>, <TokenType.DYNAMIC: 'DYNAMIC'>}
AGGREGATE_TYPE_TOKENS = {<TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>}
TYPE_TOKENS = {<TokenType.DATETIME2: 'DATETIME2'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.TIME: 'TIME'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.UNION: 'UNION'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.RING: 'RING'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.UUID: 'UUID'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>}
SIGNED_TO_UNSIGNED_TYPE_TOKEN = {<TokenType.BIGINT: 'BIGINT'>: <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.INT: 'INT'>: <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>: <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLINT: 'SMALLINT'>: <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.TINYINT: 'TINYINT'>: <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.DECIMAL: 'DECIMAL'>: <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DOUBLE: 'DOUBLE'>: <TokenType.UDOUBLE: 'UDOUBLE'>}
SUBQUERY_PREDICATES = {<TokenType.ANY: 'ANY'>: <class 'sqlglot.expressions.Any'>, <TokenType.ALL: 'ALL'>: <class 'sqlglot.expressions.All'>, <TokenType.EXISTS: 'EXISTS'>: <class 'sqlglot.expressions.Exists'>, <TokenType.SOME: 'SOME'>: <class 'sqlglot.expressions.Any'>}
RESERVED_TOKENS = {<TokenType.R_BRACKET: 'R_BRACKET'>, <TokenType.SELECT: 'SELECT'>, <TokenType.HASH: 'HASH'>, <TokenType.AMP: 'AMP'>, <TokenType.L_BRACKET: 'L_BRACKET'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.PLACEHOLDER: 'PLACEHOLDER'>, <TokenType.EQ: 'EQ'>, <TokenType.NOT: 'NOT'>, <TokenType.DASH: 'DASH'>, <TokenType.PLUS: 'PLUS'>, <TokenType.DOT: 'DOT'>, <TokenType.TILDA: 'TILDA'>, <TokenType.L_PAREN: 'L_PAREN'>, <TokenType.L_BRACE: 'L_BRACE'>, <TokenType.BACKSLASH: 'BACKSLASH'>, <TokenType.COMMA: 'COMMA'>, <TokenType.STAR: 'STAR'>, <TokenType.MOD: 'MOD'>, <TokenType.R_PAREN: 'R_PAREN'>, <TokenType.SLASH: 'SLASH'>, <TokenType.SEMICOLON: 'SEMICOLON'>, <TokenType.PIPE: 'PIPE'>, <TokenType.COLON: 'COLON'>, <TokenType.R_BRACE: 'R_BRACE'>, <TokenType.CARET: 'CARET'>, <TokenType.PARAMETER: 'PARAMETER'>, <TokenType.GT: 'GT'>, <TokenType.LT: 'LT'>}
DB_CREATABLES = {<TokenType.VIEW: 'VIEW'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.STAGE: 'STAGE'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.SINK: 'SINK'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.TABLE: 'TABLE'>, <TokenType.DATABASE: 'DATABASE'>}
CREATABLES = {<TokenType.COLUMN: 'COLUMN'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.SINK: 'SINK'>, <TokenType.INDEX: 'INDEX'>, <TokenType.TABLE: 'TABLE'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>}
ALTERABLES = {<TokenType.VIEW: 'VIEW'>, <TokenType.INDEX: 'INDEX'>, <TokenType.TABLE: 'TABLE'>}
ID_VAR_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.ASOF: 'ASOF'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ANTI: 'ANTI'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.SEMI: 'SEMI'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.FULL: 'FULL'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.APPLY: 'APPLY'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
TABLE_ALIAS_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
ALIAS_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.ASOF: 'ASOF'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ANTI: 'ANTI'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.SEMI: 'SEMI'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.FULL: 'FULL'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.APPLY: 'APPLY'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
COLON_PLACEHOLDER_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.ASOF: 'ASOF'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ANTI: 'ANTI'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.SEMI: 'SEMI'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.FULL: 'FULL'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.APPLY: 'APPLY'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
ARRAY_CONSTRUCTORS = {'ARRAY': <class 'sqlglot.expressions.Array'>, 'LIST': <class 'sqlglot.expressions.List'>}
COMMENT_TABLE_ALIAS_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
UPDATE_ALIAS_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
TRIM_TYPES = {'TRAILING', 'LEADING', 'BOTH'}
FUNC_TOKENS = {<TokenType.DATETIME2: 'DATETIME2'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.PRIMARY_KEY: 'PRIMARY_KEY'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.LIKE: 'LIKE'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.GLOB: 'GLOB'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.UNION: 'UNION'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ILIKE: 'ILIKE'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.XOR: 'XOR'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.INSERT: 'INSERT'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.RLIKE: 'RLIKE'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>}
CONJUNCTION: Dict[sqlglot.tokens.TokenType, Type[sqlglot.expressions.Expression]] = {<TokenType.AND: 'AND'>: <class 'sqlglot.expressions.And'>}
ASSIGNMENT: Dict[sqlglot.tokens.TokenType, Type[sqlglot.expressions.Expression]] = {<TokenType.COLON_EQ: 'COLON_EQ'>: <class 'sqlglot.expressions.PropertyEQ'>}
DISJUNCTION: Dict[sqlglot.tokens.TokenType, Type[sqlglot.expressions.Expression]] = {<TokenType.OR: 'OR'>: <class 'sqlglot.expressions.Or'>}
EQUALITY = {<TokenType.EQ: 'EQ'>: <class 'sqlglot.expressions.EQ'>, <TokenType.NEQ: 'NEQ'>: <class 'sqlglot.expressions.NEQ'>, <TokenType.NULLSAFE_EQ: 'NULLSAFE_EQ'>: <class 'sqlglot.expressions.NullSafeEQ'>}
COMPARISON = {<TokenType.GT: 'GT'>: <class 'sqlglot.expressions.GT'>, <TokenType.GTE: 'GTE'>: <class 'sqlglot.expressions.GTE'>, <TokenType.LT: 'LT'>: <class 'sqlglot.expressions.LT'>, <TokenType.LTE: 'LTE'>: <class 'sqlglot.expressions.LTE'>}
BITWISE = {<TokenType.AMP: 'AMP'>: <class 'sqlglot.expressions.BitwiseAnd'>, <TokenType.CARET: 'CARET'>: <class 'sqlglot.expressions.BitwiseXor'>, <TokenType.PIPE: 'PIPE'>: <class 'sqlglot.expressions.BitwiseOr'>}
TERM = {<TokenType.DASH: 'DASH'>: <class 'sqlglot.expressions.Sub'>, <TokenType.PLUS: 'PLUS'>: <class 'sqlglot.expressions.Add'>, <TokenType.MOD: 'MOD'>: <class 'sqlglot.expressions.Mod'>, <TokenType.COLLATE: 'COLLATE'>: <class 'sqlglot.expressions.Collate'>}
FACTOR = {<TokenType.DIV: 'DIV'>: <class 'sqlglot.expressions.IntDiv'>, <TokenType.LR_ARROW: 'LR_ARROW'>: <class 'sqlglot.expressions.Distance'>, <TokenType.SLASH: 'SLASH'>: <class 'sqlglot.expressions.Div'>, <TokenType.STAR: 'STAR'>: <class 'sqlglot.expressions.Mul'>}
TIMES = {<TokenType.TIME: 'TIME'>, <TokenType.TIMETZ: 'TIMETZ'>}
TIMESTAMPS = {<TokenType.TIME: 'TIME'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>}
SET_OPERATIONS = {<TokenType.EXCEPT: 'EXCEPT'>, <TokenType.INTERSECT: 'INTERSECT'>, <TokenType.UNION: 'UNION'>}
JOIN_METHODS = {<TokenType.POSITIONAL: 'POSITIONAL'>, <TokenType.ASOF: 'ASOF'>, <TokenType.NATURAL: 'NATURAL'>}
JOIN_SIDES = {<TokenType.LEFT: 'LEFT'>, <TokenType.FULL: 'FULL'>, <TokenType.RIGHT: 'RIGHT'>}
JOIN_KINDS = {<TokenType.OUTER: 'OUTER'>, <TokenType.STRAIGHT_JOIN: 'STRAIGHT_JOIN'>, <TokenType.CROSS: 'CROSS'>, <TokenType.INNER: 'INNER'>, <TokenType.SEMI: 'SEMI'>, <TokenType.ANTI: 'ANTI'>}
JOIN_HINTS: Set[str] = set()
LAMBDAS = {<TokenType.ARROW: 'ARROW'>: <function Parser.<lambda>>, <TokenType.FARROW: 'FARROW'>: <function Parser.<lambda>>}
COLUMN_OPERATORS = {<TokenType.DOT: 'DOT'>: None, <TokenType.DOTCOLON: 'DOTCOLON'>: <function Parser.<lambda>>, <TokenType.DCOLON: 'DCOLON'>: <function Parser.<lambda>>, <TokenType.ARROW: 'ARROW'>: <function Parser.<lambda>>, <TokenType.DARROW: 'DARROW'>: <function Parser.<lambda>>, <TokenType.HASH_ARROW: 'HASH_ARROW'>: <function Parser.<lambda>>, <TokenType.DHASH_ARROW: 'DHASH_ARROW'>: <function Parser.<lambda>>, <TokenType.PLACEHOLDER: 'PLACEHOLDER'>: <function Parser.<lambda>>}
EXPRESSION_PARSERS = {<class 'sqlglot.expressions.Cluster'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Column'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Condition'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.DataType'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Expression'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.From'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Group'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Having'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Hint'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Identifier'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Join'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Lambda'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Lateral'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Limit'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Offset'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Order'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Ordered'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Properties'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.PartitionedByProperty'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Qualify'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Returning'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Select'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Sort'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Table'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.TableAlias'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Tuple'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Whens'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Where'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.Window'>: <function Parser.<lambda>>, <class 'sqlglot.expressions.With'>: <function Parser.<lambda>>, 'JOIN_TYPE': <function Parser.<lambda>>}
STATEMENT_PARSERS = {<TokenType.ALTER: 'ALTER'>: <function Parser.<lambda>>, <TokenType.ANALYZE: 'ANALYZE'>: <function Parser.<lambda>>, <TokenType.BEGIN: 'BEGIN'>: <function Parser.<lambda>>, <TokenType.CACHE: 'CACHE'>: <function Parser.<lambda>>, <TokenType.COMMENT: 'COMMENT'>: <function Parser.<lambda>>, <TokenType.COMMIT: 'COMMIT'>: <function Parser.<lambda>>, <TokenType.COPY: 'COPY'>: <function Parser.<lambda>>, <TokenType.CREATE: 'CREATE'>: <function Parser.<lambda>>, <TokenType.DELETE: 'DELETE'>: <function Parser.<lambda>>, <TokenType.DESC: 'DESC'>: <function Parser.<lambda>>, <TokenType.DESCRIBE: 'DESCRIBE'>: <function Parser.<lambda>>, <TokenType.DROP: 'DROP'>: <function Parser.<lambda>>, <TokenType.GRANT: 'GRANT'>: <function Parser.<lambda>>, <TokenType.INSERT: 'INSERT'>: <function Parser.<lambda>>, <TokenType.KILL: 'KILL'>: <function Parser.<lambda>>, <TokenType.LOAD: 'LOAD'>: <function Parser.<lambda>>, <TokenType.MERGE: 'MERGE'>: <function Parser.<lambda>>, <TokenType.PIVOT: 'PIVOT'>: <function Parser.<lambda>>, <TokenType.PRAGMA: 'PRAGMA'>: <function Parser.<lambda>>, <TokenType.REFRESH: 'REFRESH'>: <function Parser.<lambda>>, <TokenType.ROLLBACK: 'ROLLBACK'>: <function Parser.<lambda>>, <TokenType.SET: 'SET'>: <function Parser.<lambda>>, <TokenType.TRUNCATE: 'TRUNCATE'>: <function Parser.<lambda>>, <TokenType.UNCACHE: 'UNCACHE'>: <function Parser.<lambda>>, <TokenType.UNPIVOT: 'UNPIVOT'>: <function Parser.<lambda>>, <TokenType.UPDATE: 'UPDATE'>: <function Parser.<lambda>>, <TokenType.USE: 'USE'>: <function Parser.<lambda>>, <TokenType.SEMICOLON: 'SEMICOLON'>: <function Parser.<lambda>>}
UNARY_PARSERS = {<TokenType.PLUS: 'PLUS'>: <function Parser.<lambda>>, <TokenType.NOT: 'NOT'>: <function Parser.<lambda>>, <TokenType.TILDA: 'TILDA'>: <function Parser.<lambda>>, <TokenType.DASH: 'DASH'>: <function Parser.<lambda>>, <TokenType.PIPE_SLASH: 'PIPE_SLASH'>: <function Parser.<lambda>>, <TokenType.DPIPE_SLASH: 'DPIPE_SLASH'>: <function Parser.<lambda>>}
STRING_PARSERS = {<TokenType.HEREDOC_STRING: 'HEREDOC_STRING'>: <function Parser.<lambda>>, <TokenType.NATIONAL_STRING: 'NATIONAL_STRING'>: <function Parser.<lambda>>, <TokenType.RAW_STRING: 'RAW_STRING'>: <function Parser.<lambda>>, <TokenType.STRING: 'STRING'>: <function Parser.<lambda>>, <TokenType.UNICODE_STRING: 'UNICODE_STRING'>: <function Parser.<lambda>>}
NUMERIC_PARSERS = {<TokenType.BIT_STRING: 'BIT_STRING'>: <function Parser.<lambda>>, <TokenType.BYTE_STRING: 'BYTE_STRING'>: <function Parser.<lambda>>, <TokenType.HEX_STRING: 'HEX_STRING'>: <function Parser.<lambda>>, <TokenType.NUMBER: 'NUMBER'>: <function Parser.<lambda>>}
PRIMARY_PARSERS = {<TokenType.HEREDOC_STRING: 'HEREDOC_STRING'>: <function Parser.<lambda>>, <TokenType.NATIONAL_STRING: 'NATIONAL_STRING'>: <function Parser.<lambda>>, <TokenType.RAW_STRING: 'RAW_STRING'>: <function Parser.<lambda>>, <TokenType.STRING: 'STRING'>: <function Parser.<lambda>>, <TokenType.UNICODE_STRING: 'UNICODE_STRING'>: <function Parser.<lambda>>, <TokenType.BIT_STRING: 'BIT_STRING'>: <function Parser.<lambda>>, <TokenType.BYTE_STRING: 'BYTE_STRING'>: <function Parser.<lambda>>, <TokenType.HEX_STRING: 'HEX_STRING'>: <function Parser.<lambda>>, <TokenType.NUMBER: 'NUMBER'>: <function Parser.<lambda>>, <TokenType.INTRODUCER: 'INTRODUCER'>: <function Parser.<lambda>>, <TokenType.NULL: 'NULL'>: <function Parser.<lambda>>, <TokenType.TRUE: 'TRUE'>: <function Parser.<lambda>>, <TokenType.FALSE: 'FALSE'>: <function Parser.<lambda>>, <TokenType.SESSION_PARAMETER: 'SESSION_PARAMETER'>: <function Parser.<lambda>>, <TokenType.STAR: 'STAR'>: <function Parser.<lambda>>}
PLACEHOLDER_PARSERS = {<TokenType.PLACEHOLDER: 'PLACEHOLDER'>: <function Parser.<lambda>>, <TokenType.PARAMETER: 'PARAMETER'>: <function Parser.<lambda>>, <TokenType.COLON: 'COLON'>: <function Parser.<lambda>>}
RANGE_PARSERS = {<TokenType.AT_GT: 'AT_GT'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.BETWEEN: 'BETWEEN'>: <function Parser.<lambda>>, <TokenType.GLOB: 'GLOB'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.ILIKE: 'ILIKE'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.IN: 'IN'>: <function Parser.<lambda>>, <TokenType.IRLIKE: 'IRLIKE'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.IS: 'IS'>: <function Parser.<lambda>>, <TokenType.LIKE: 'LIKE'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.LT_AT: 'LT_AT'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.OVERLAPS: 'OVERLAPS'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.RLIKE: 'RLIKE'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.SIMILAR_TO: 'SIMILAR_TO'>: <function binary_range_parser.<locals>._parse_binary_range>, <TokenType.FOR: 'FOR'>: <function Parser.<lambda>>}
PROPERTY_PARSERS: Dict[str, Callable] = {'ALLOWED_VALUES': <function Parser.<lambda>>, 'ALGORITHM': <function Parser.<lambda>>, 'AUTO': <function Parser.<lambda>>, 'AUTO_INCREMENT': <function Parser.<lambda>>, 'BACKUP': <function Parser.<lambda>>, 'BLOCKCOMPRESSION': <function Parser.<lambda>>, 'CHARSET': <function Parser.<lambda>>, 'CHARACTER SET': <function Parser.<lambda>>, 'CHECKSUM': <function Parser.<lambda>>, 'CLUSTER BY': <function Parser.<lambda>>, 'CLUSTERED': <function Parser.<lambda>>, 'COLLATE': <function Parser.<lambda>>, 'COMMENT': <function Parser.<lambda>>, 'CONTAINS': <function Parser.<lambda>>, 'COPY': <function Parser.<lambda>>, 'DATABLOCKSIZE': <function Parser.<lambda>>, 'DATA_DELETION': <function Parser.<lambda>>, 'DEFINER': <function Parser.<lambda>>, 'DETERMINISTIC': <function Parser.<lambda>>, 'DISTRIBUTED': <function Parser.<lambda>>, 'DUPLICATE': <function Parser.<lambda>>, 'DYNAMIC': <function Parser.<lambda>>, 'DISTKEY': <function Parser.<lambda>>, 'DISTSTYLE': <function Parser.<lambda>>, 'EMPTY': <function Parser.<lambda>>, 'ENGINE': <function Parser.<lambda>>, 'ENVIRONMENT': <function Parser.<lambda>>, 'EXECUTE': <function Parser.<lambda>>, 'EXTERNAL': <function Parser.<lambda>>, 'FALLBACK': <function Parser.<lambda>>, 'FORMAT': <function Parser.<lambda>>, 'FREESPACE': <function Parser.<lambda>>, 'GLOBAL': <function Parser.<lambda>>, 'HEAP': <function Parser.<lambda>>, 'ICEBERG': <function Parser.<lambda>>, 'IMMUTABLE': <function Parser.<lambda>>, 'INHERITS': <function Parser.<lambda>>, 'INPUT': <function Parser.<lambda>>, 'JOURNAL': <function Parser.<lambda>>, 'LANGUAGE': <function Parser.<lambda>>, 'LAYOUT': <function Parser.<lambda>>, 'LIFETIME': <function Parser.<lambda>>, 'LIKE': <function Parser.<lambda>>, 'LOCATION': <function Parser.<lambda>>, 'LOCK': <function Parser.<lambda>>, 'LOCKING': <function Parser.<lambda>>, 'LOG': <function Parser.<lambda>>, 'MATERIALIZED': <function Parser.<lambda>>, 'MERGEBLOCKRATIO': <function Parser.<lambda>>, 'MODIFIES': <function Parser.<lambda>>, 'MULTISET': <function Parser.<lambda>>, 'NO': <function Parser.<lambda>>, 'ON': <function Parser.<lambda>>, 'ORDER BY': <function Parser.<lambda>>, 'OUTPUT': <function Parser.<lambda>>, 'PARTITION': <function Parser.<lambda>>, 'PARTITION BY': <function Parser.<lambda>>, 'PARTITIONED BY': <function Parser.<lambda>>, 'PARTITIONED_BY': <function Parser.<lambda>>, 'PRIMARY KEY': <function Parser.<lambda>>, 'RANGE': <function Parser.<lambda>>, 'READS': <function Parser.<lambda>>, 'REMOTE': <function Parser.<lambda>>, 'RETURNS': <function Parser.<lambda>>, 'STRICT': <function Parser.<lambda>>, 'STREAMING': <function Parser.<lambda>>, 'ROW': <function Parser.<lambda>>, 'ROW_FORMAT': <function Parser.<lambda>>, 'SAMPLE': <function Parser.<lambda>>, 'SECURE': <function Parser.<lambda>>, 'SECURITY': <function Parser.<lambda>>, 'SET': <function Parser.<lambda>>, 'SETTINGS': <function Parser.<lambda>>, 'SHARING': <function Parser.<lambda>>, 'SORTKEY': <function Parser.<lambda>>, 'SOURCE': <function Parser.<lambda>>, 'STABLE': <function Parser.<lambda>>, 'STORED': <function Parser.<lambda>>, 'SYSTEM_VERSIONING': <function Parser.<lambda>>, 'TBLPROPERTIES': <function Parser.<lambda>>, 'TEMP': <function Parser.<lambda>>, 'TEMPORARY': <function Parser.<lambda>>, 'TO': <function Parser.<lambda>>, 'TRANSIENT': <function Parser.<lambda>>, 'TRANSFORM': <function Parser.<lambda>>, 'TTL': <function Parser.<lambda>>, 'USING': <function Parser.<lambda>>, 'UNLOGGED': <function Parser.<lambda>>, 'VOLATILE': <function Parser.<lambda>>, 'WITH': <function Parser.<lambda>>}
CONSTRAINT_PARSERS = {'AUTOINCREMENT': <function Parser.<lambda>>, 'AUTO_INCREMENT': <function Parser.<lambda>>, 'CASESPECIFIC': <function Parser.<lambda>>, 'CHARACTER SET': <function Parser.<lambda>>, 'CHECK': <function Parser.<lambda>>, 'COLLATE': <function Parser.<lambda>>, 'COMMENT': <function Parser.<lambda>>, 'COMPRESS': <function Parser.<lambda>>, 'CLUSTERED': <function Parser.<lambda>>, 'NONCLUSTERED': <function Parser.<lambda>>, 'DEFAULT': <function Parser.<lambda>>, 'ENCODE': <function Parser.<lambda>>, 'EPHEMERAL': <function Parser.<lambda>>, 'EXCLUDE': <function Parser.<lambda>>, 'FOREIGN KEY': <function Parser.<lambda>>, 'FORMAT': <function Parser.<lambda>>, 'GENERATED': <function Parser.<lambda>>, 'IDENTITY': <function Parser.<lambda>>, 'INLINE': <function Parser.<lambda>>, 'LIKE': <function Parser.<lambda>>, 'NOT': <function Parser.<lambda>>, 'NULL': <function Parser.<lambda>>, 'ON': <function Parser.<lambda>>, 'PATH': <function Parser.<lambda>>, 'PERIOD': <function Parser.<lambda>>, 'PRIMARY KEY': <function Parser.<lambda>>, 'REFERENCES': <function Parser.<lambda>>, 'TITLE': <function Parser.<lambda>>, 'TTL': <function Parser.<lambda>>, 'UNIQUE': <function Parser.<lambda>>, 'UPPERCASE': <function Parser.<lambda>>, 'WATERMARK': <function Parser.<lambda>>, 'WITH': <function Parser.<lambda>>, 'BUCKET': <function Parser.<lambda>>, 'TRUNCATE': <function Parser.<lambda>>}
ALTER_PARSERS = {'ADD': <function Parser.<lambda>>, 'AS': <function Parser.<lambda>>, 'ALTER': <function Parser.<lambda>>, 'CLUSTER BY': <function Parser.<lambda>>, 'DELETE': <function Parser.<lambda>>, 'DROP': <function Parser.<lambda>>, 'RENAME': <function Parser.<lambda>>, 'SET': <function Parser.<lambda>>, 'SWAP': <function Parser.<lambda>>}
ALTER_ALTER_PARSERS = {'DISTKEY': <function Parser.<lambda>>, 'DISTSTYLE': <function Parser.<lambda>>, 'SORTKEY': <function Parser.<lambda>>, 'COMPOUND': <function Parser.<lambda>>}
SCHEMA_UNNAMED_CONSTRAINTS = {'BUCKET', 'TRUNCATE', 'UNIQUE', 'CHECK', 'EXCLUDE', 'PRIMARY KEY', 'PERIOD', 'LIKE', 'FOREIGN KEY', 'WATERMARK'}
NO_PAREN_FUNCTION_PARSERS = {'ANY': <function Parser.<lambda>>, 'CASE': <function Parser.<lambda>>, 'CONNECT_BY_ROOT': <function Parser.<lambda>>, 'IF': <function Parser.<lambda>>}
INVALID_FUNC_NAME_TOKENS = {<TokenType.STRING: 'STRING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>}
FUNCTIONS_WITH_ALIASED_ARGS = {'STRUCT'}
KEY_VALUE_DEFINITIONS = (<class 'sqlglot.expressions.Alias'>, <class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
FUNCTION_PARSERS = {'ARG_MAX': <function Parser.<dictcomp>.<lambda>>, 'ARGMAX': <function Parser.<dictcomp>.<lambda>>, 'MAX_BY': <function Parser.<dictcomp>.<lambda>>, 'ARG_MIN': <function Parser.<dictcomp>.<lambda>>, 'ARGMIN': <function Parser.<dictcomp>.<lambda>>, 'MIN_BY': <function Parser.<dictcomp>.<lambda>>, 'CAST': <function Parser.<lambda>>, 'CEIL': <function Parser.<lambda>>, 'CONVERT': <function Parser.<lambda>>, 'DECODE': <function Parser.<lambda>>, 'EXTRACT': <function Parser.<lambda>>, 'FLOOR': <function Parser.<lambda>>, 'GAP_FILL': <function Parser.<lambda>>, 'JSON_OBJECT': <function Parser.<lambda>>, 'JSON_OBJECTAGG': <function Parser.<lambda>>, 'JSON_TABLE': <function Parser.<lambda>>, 'MATCH': <function Parser.<lambda>>, 'NORMALIZE': <function Parser.<lambda>>, 'OPENJSON': <function Parser.<lambda>>, 'OVERLAY': <function Parser.<lambda>>, 'POSITION': <function Parser.<lambda>>, 'PREDICT': <function Parser.<lambda>>, 'SAFE_CAST': <function Parser.<lambda>>, 'STRING_AGG': <function Parser.<lambda>>, 'SUBSTRING': <function Parser.<lambda>>, 'TRIM': <function Parser.<lambda>>, 'TRY_CAST': <function Parser.<lambda>>, 'TRY_CONVERT': <function Parser.<lambda>>, 'XMLELEMENT': <function Parser.<lambda>>, 'XMLTABLE': <function Parser.<lambda>>}
QUERY_MODIFIER_PARSERS = {<TokenType.MATCH_RECOGNIZE: 'MATCH_RECOGNIZE'>: <function Parser.<lambda>>, <TokenType.PREWHERE: 'PREWHERE'>: <function Parser.<lambda>>, <TokenType.WHERE: 'WHERE'>: <function Parser.<lambda>>, <TokenType.GROUP_BY: 'GROUP_BY'>: <function Parser.<lambda>>, <TokenType.HAVING: 'HAVING'>: <function Parser.<lambda>>, <TokenType.QUALIFY: 'QUALIFY'>: <function Parser.<lambda>>, <TokenType.WINDOW: 'WINDOW'>: <function Parser.<lambda>>, <TokenType.ORDER_BY: 'ORDER_BY'>: <function Parser.<lambda>>, <TokenType.LIMIT: 'LIMIT'>: <function Parser.<lambda>>, <TokenType.FETCH: 'FETCH'>: <function Parser.<lambda>>, <TokenType.OFFSET: 'OFFSET'>: <function Parser.<lambda>>, <TokenType.FOR: 'FOR'>: <function Parser.<lambda>>, <TokenType.LOCK: 'LOCK'>: <function Parser.<lambda>>, <TokenType.TABLE_SAMPLE: 'TABLE_SAMPLE'>: <function Parser.<lambda>>, <TokenType.USING: 'USING'>: <function Parser.<lambda>>, <TokenType.CLUSTER_BY: 'CLUSTER_BY'>: <function Parser.<lambda>>, <TokenType.DISTRIBUTE_BY: 'DISTRIBUTE_BY'>: <function Parser.<lambda>>, <TokenType.SORT_BY: 'SORT_BY'>: <function Parser.<lambda>>, <TokenType.CONNECT_BY: 'CONNECT_BY'>: <function Parser.<lambda>>, <TokenType.START_WITH: 'START_WITH'>: <function Parser.<lambda>>}
SET_PARSERS = {'GLOBAL': <function Parser.<lambda>>, 'LOCAL': <function Parser.<lambda>>, 'SESSION': <function Parser.<lambda>>, 'TRANSACTION': <function Parser.<lambda>>}
SHOW_PARSERS: Dict[str, Callable] = {}
TYPE_LITERAL_PARSERS = {<Type.JSON: 'JSON'>: <function Parser.<lambda>>}
DDL_SELECT_TOKENS = {<TokenType.L_PAREN: 'L_PAREN'>, <TokenType.SELECT: 'SELECT'>, <TokenType.WITH: 'WITH'>}
PRE_VOLATILE_TOKENS = {<TokenType.CREATE: 'CREATE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.REPLACE: 'REPLACE'>}
TRANSACTION_KIND = {'DEFERRED', 'EXCLUSIVE', 'IMMEDIATE'}
TRANSACTION_CHARACTERISTICS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'ISOLATION': (('LEVEL', 'REPEATABLE', 'READ'), ('LEVEL', 'READ', 'COMMITTED'), ('LEVEL', 'READ', 'UNCOMITTED'), ('LEVEL', 'SERIALIZABLE')), 'READ': ('WRITE', 'ONLY')}
CONFLICT_ACTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'ABORT': (), 'FAIL': (), 'IGNORE': (), 'REPLACE': (), 'ROLLBACK': (), 'UPDATE': (), 'DO': ('NOTHING', 'UPDATE')}
CREATE_SEQUENCE: Dict[str, Sequence[Union[Sequence[str], str]]] = {'SCALE': ('EXTEND', 'NOEXTEND'), 'SHARD': ('EXTEND', 'NOEXTEND'), 'NO': ('CYCLE', 'CACHE', 'MAXVALUE', 'MINVALUE'), 'SESSION': (), 'GLOBAL': (), 'KEEP': (), 'NOKEEP': (), 'ORDER': (), 'NOORDER': (), 'NOCACHE': (), 'CYCLE': (), 'NOCYCLE': (), 'NOMINVALUE': (), 'NOMAXVALUE': (), 'NOSCALE': (), 'NOSHARD': ()}
ISOLATED_LOADING_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'FOR': ('ALL', 'INSERT', 'NONE')}
USABLES: Dict[str, Sequence[Union[Sequence[str], str]]] = {'ROLE': (), 'WAREHOUSE': (), 'DATABASE': (), 'SCHEMA': (), 'CATALOG': ()}
CAST_ACTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'RENAME': ('FIELDS',), 'ADD': ('FIELDS',)}
SCHEMA_BINDING_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'TYPE': ('EVOLUTION',), 'BINDING': (), 'COMPENSATION': (), 'EVOLUTION': ()}
PROCEDURE_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {}
EXECUTE_AS_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'CALLER': (), 'SELF': (), 'OWNER': ()}
KEY_CONSTRAINT_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'NOT': ('ENFORCED',), 'MATCH': ('FULL', 'PARTIAL', 'SIMPLE'), 'INITIALLY': ('DEFERRED', 'IMMEDIATE'), 'USING': ('BTREE', 'HASH'), 'DEFERRABLE': (), 'NORELY': (), 'RELY': ()}
WINDOW_EXCLUDE_OPTIONS: Dict[str, Sequence[Union[Sequence[str], str]]] = {'NO': ('OTHERS',), 'CURRENT': ('ROW',), 'GROUP': (), 'TIES': ()}
INSERT_ALTERNATIVES = {'ROLLBACK', 'REPLACE', 'IGNORE', 'ABORT', 'FAIL'}
CLONE_KEYWORDS = {'CLONE', 'COPY'}
HISTORICAL_DATA_PREFIX = {'END', 'BEFORE', 'AT'}
HISTORICAL_DATA_KIND = {'STATEMENT', 'OFFSET', 'TIMESTAMP', 'STREAM'}
OPCLASS_FOLLOW_KEYWORDS = {'ASC', 'NULLS', 'DESC', 'WITH'}
OPTYPE_FOLLOW_TOKENS = {<TokenType.R_PAREN: 'R_PAREN'>, <TokenType.COMMA: 'COMMA'>}
TABLE_INDEX_HINT_TOKENS = {<TokenType.IGNORE: 'IGNORE'>, <TokenType.FORCE: 'FORCE'>, <TokenType.USE: 'USE'>}
VIEW_ATTRIBUTES = {'ENCRYPTION', 'SCHEMABINDING', 'VIEW_METADATA'}
WINDOW_ALIAS_TOKENS = {<TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.ASOF: 'ASOF'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ANTI: 'ANTI'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.SEMI: 'SEMI'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.FULL: 'FULL'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.APPLY: 'APPLY'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
WINDOW_BEFORE_PAREN_TOKENS = {<TokenType.OVER: 'OVER'>}
WINDOW_SIDES = {'FOLLOWING', 'PRECEDING'}
JSON_KEY_VALUE_SEPARATOR_TOKENS = {<TokenType.COLON: 'COLON'>, <TokenType.COMMA: 'COMMA'>, <TokenType.IS: 'IS'>}
FETCH_TOKENS = {<TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.ASOF: 'ASOF'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ANTI: 'ANTI'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.LEFT: 'LEFT'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.SEMI: 'SEMI'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.FULL: 'FULL'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.APPLY: 'APPLY'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
ADD_CONSTRAINT_TOKENS = {<TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.PRIMARY_KEY: 'PRIMARY_KEY'>, <TokenType.KEY: 'KEY'>, <TokenType.INDEX: 'INDEX'>}
DISTINCT_TOKENS = {<TokenType.DISTINCT: 'DISTINCT'>}
NULL_TOKENS = {<TokenType.NULL: 'NULL'>}
UNNEST_OFFSET_ALIAS_TOKENS = {<TokenType.ROWS: 'ROWS'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CUBE: 'CUBE'>, <TokenType.INT128: 'INT128'>, <TokenType.INT: 'INT'>, <TokenType.RANGE: 'RANGE'>, <TokenType.ALL: 'ALL'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TOP: 'TOP'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.BIT: 'BIT'>, <TokenType.BINARY: 'BINARY'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SOME: 'SOME'>, <TokenType.TIME: 'TIME'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.DESCRIBE: 'DESCRIBE'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.PUT: 'PUT'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.FALSE: 'FALSE'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.DELETE: 'DELETE'>, <TokenType.STAGE: 'STAGE'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.FIRST: 'FIRST'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.XML: 'XML'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.UINT: 'UINT'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.VAR: 'VAR'>, <TokenType.IS: 'IS'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.JSON: 'JSON'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.NEXT: 'NEXT'>, <TokenType.RING: 'RING'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.SET: 'SET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.IPV6: 'IPV6'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.SUPER: 'SUPER'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TABLE: 'TABLE'>, <TokenType.FILTER: 'FILTER'>, <TokenType.BLOB: 'BLOB'>, <TokenType.TRUE: 'TRUE'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.CASE: 'CASE'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.GET: 'GET'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.LIST: 'LIST'>, <TokenType.NULL: 'NULL'>, <TokenType.FINAL: 'FINAL'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MAP: 'MAP'>, <TokenType.MERGE: 'MERGE'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.UINT128: 'UINT128'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.END: 'END'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.LOAD: 'LOAD'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.YEAR: 'YEAR'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.TAG: 'TAG'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.ROW: 'ROW'>, <TokenType.UUID: 'UUID'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.DATE32: 'DATE32'>, <TokenType.INT256: 'INT256'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.VIEW: 'VIEW'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.VOID: 'VOID'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.KILL: 'KILL'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.KEEP: 'KEEP'>, <TokenType.COPY: 'COPY'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.NESTED: 'NESTED'>, <TokenType.CACHE: 'CACHE'>, <TokenType.USE: 'USE'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.IPV4: 'IPV4'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.DESC: 'DESC'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.CHAR: 'CHAR'>, <TokenType.POINT: 'POINT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.DIV: 'DIV'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.UINT256: 'UINT256'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.ANY: 'ANY'>, <TokenType.DATE: 'DATE'>, <TokenType.TEXT: 'TEXT'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.ASC: 'ASC'>, <TokenType.INET: 'INET'>, <TokenType.NAME: 'NAME'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.ENUM: 'ENUM'>, <TokenType.SHOW: 'SHOW'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.SINK: 'SINK'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.COMMIT: 'COMMIT'>}
SELECT_START_TOKENS = {<TokenType.L_PAREN: 'L_PAREN'>, <TokenType.WITH: 'WITH'>, <TokenType.SELECT: 'SELECT'>}
COPY_INTO_VARLEN_OPTIONS = {'CREDENTIAL', 'FORMAT_OPTIONS', 'COPY_OPTIONS', 'FILE_FORMAT'}
IS_JSON_PREDICATE_KIND = {'SCALAR', 'ARRAY', 'VALUE', 'OBJECT'}
ODBC_DATETIME_LITERALS = {'d': <class 'sqlglot.expressions.Date'>, 't': <class 'sqlglot.expressions.Time'>, 'ts': <class 'sqlglot.expressions.Timestamp'>}
ON_CONDITION_TOKENS = {'TRUE', 'EMPTY', 'NULL', 'ERROR', 'FALSE'}
PRIVILEGE_FOLLOW_TOKENS = {<TokenType.L_PAREN: 'L_PAREN'>, <TokenType.ON: 'ON'>, <TokenType.COMMA: 'COMMA'>}
DESCRIBE_STYLES = {'FORMATTED', 'HISTORY', 'ANALYZE', 'EXTENDED'}
ANALYZE_STYLES = {'VERBOSE', 'SKIP_LOCKED', 'FULL', 'SAMPLE', 'BUFFER_USAGE_LIMIT', 'NO_WRITE_TO_BINLOG', 'LOCAL'}
ANALYZE_EXPRESSION_PARSERS = {'ALL': <function Parser.<lambda>>, 'COMPUTE': <function Parser.<lambda>>, 'DELETE': <function Parser.<lambda>>, 'DROP': <function Parser.<lambda>>, 'ESTIMATE': <function Parser.<lambda>>, 'LIST': <function Parser.<lambda>>, 'PREDICATE': <function Parser.<lambda>>, 'UPDATE': <function Parser.<lambda>>, 'VALIDATE': <function Parser.<lambda>>}
PARTITION_KEYWORDS = {'SUBPARTITION', 'PARTITION'}
AMBIGUOUS_ALIAS_TOKENS = (<TokenType.LIMIT: 'LIMIT'>, <TokenType.OFFSET: 'OFFSET'>)
OPERATION_MODIFIERS: Set[str] = set()
RECURSIVE_CTE_SEARCH_KIND = {'DEPTH', 'CYCLE', 'BREADTH'}
STRICT_CAST = True
PREFIXED_PIVOT_COLUMNS = False
IDENTIFY_PIVOT_STRINGS = False
LOG_DEFAULTS_TO_LN = False
ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN = True
TABLESAMPLE_CSV = False
DEFAULT_SAMPLING_METHOD: Optional[str] = None
SET_REQUIRES_ASSIGNMENT_DELIMITER = True
TRIM_PATTERN_FIRST = False
STRING_ALIASES = False
MODIFIERS_ATTACHED_TO_SET_OP = True
SET_OP_MODIFIERS = {'limit', 'offset', 'order'}
NO_PAREN_IF_COMMANDS = True
JSON_ARROWS_REQUIRE_JSON_TYPE = False
COLON_IS_VARIANT_EXTRACT = False
VALUES_FOLLOWED_BY_PAREN = True
SUPPORTS_IMPLICIT_UNNEST = False
INTERVAL_SPANS = True
SUPPORTS_PARTITION_SELECTION = False
WRAPPED_TRANSFORM_COLUMN_CONSTRAINT = True
OPTIONAL_ALIAS_TOKEN_CTE = True
SHOW_TRIE: Dict = {}
SET_TRIE: Dict = {'GLOBAL': {0: True}, 'LOCAL': {0: True}, 'SESSION': {0: True}, 'TRANSACTION': {0: True}}
error_level
error_message_context
max_errors
dialect
def reset(self):
1535    def reset(self):
1536        self.sql = ""
1537        self.errors = []
1538        self._tokens = []
1539        self._index = 0
1540        self._curr = None
1541        self._next = None
1542        self._prev = None
1543        self._prev_comments = None
def parse( self, raw_tokens: List[sqlglot.tokens.Token], sql: Optional[str] = None) -> List[Optional[sqlglot.expressions.Expression]]:
1545    def parse(
1546        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1547    ) -> t.List[t.Optional[exp.Expression]]:
1548        """
1549        Parses a list of tokens and returns a list of syntax trees, one tree
1550        per parsed SQL statement.
1551
1552        Args:
1553            raw_tokens: The list of tokens.
1554            sql: The original SQL string, used to produce helpful debug messages.
1555
1556        Returns:
1557            The list of the produced syntax trees.
1558        """
1559        return self._parse(
1560            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1561        )

Parses a list of tokens and returns a list of syntax trees, one tree per parsed SQL statement.

Arguments:
  • raw_tokens: The list of tokens.
  • sql: The original SQL string, used to produce helpful debug messages.
Returns:

The list of the produced syntax trees.

def parse_into( self, expression_types: Union[str, Type[sqlglot.expressions.Expression], Collection[Union[str, Type[sqlglot.expressions.Expression]]]], raw_tokens: List[sqlglot.tokens.Token], sql: Optional[str] = None) -> List[Optional[sqlglot.expressions.Expression]]:
1563    def parse_into(
1564        self,
1565        expression_types: exp.IntoType,
1566        raw_tokens: t.List[Token],
1567        sql: t.Optional[str] = None,
1568    ) -> t.List[t.Optional[exp.Expression]]:
1569        """
1570        Parses a list of tokens into a given Expression type. If a collection of Expression
1571        types is given instead, this method will try to parse the token list into each one
1572        of them, stopping at the first for which the parsing succeeds.
1573
1574        Args:
1575            expression_types: The expression type(s) to try and parse the token list into.
1576            raw_tokens: The list of tokens.
1577            sql: The original SQL string, used to produce helpful debug messages.
1578
1579        Returns:
1580            The target Expression.
1581        """
1582        errors = []
1583        for expression_type in ensure_list(expression_types):
1584            parser = self.EXPRESSION_PARSERS.get(expression_type)
1585            if not parser:
1586                raise TypeError(f"No parser registered for {expression_type}")
1587
1588            try:
1589                return self._parse(parser, raw_tokens, sql)
1590            except ParseError as e:
1591                e.errors[0]["into_expression"] = expression_type
1592                errors.append(e)
1593
1594        raise ParseError(
1595            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1596            errors=merge_errors(errors),
1597        ) from errors[-1]

Parses a list of tokens into a given Expression type. If a collection of Expression types is given instead, this method will try to parse the token list into each one of them, stopping at the first for which the parsing succeeds.

Arguments:
  • expression_types: The expression type(s) to try and parse the token list into.
  • raw_tokens: The list of tokens.
  • sql: The original SQL string, used to produce helpful debug messages.
Returns:

The target Expression.

def check_errors(self) -> None:
1637    def check_errors(self) -> None:
1638        """Logs or raises any found errors, depending on the chosen error level setting."""
1639        if self.error_level == ErrorLevel.WARN:
1640            for error in self.errors:
1641                logger.error(str(error))
1642        elif self.error_level == ErrorLevel.RAISE and self.errors:
1643            raise ParseError(
1644                concat_messages(self.errors, self.max_errors),
1645                errors=merge_errors(self.errors),
1646            )

Logs or raises any found errors, depending on the chosen error level setting.

def raise_error(self, message: str, token: Optional[sqlglot.tokens.Token] = None) -> None:
1648    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1649        """
1650        Appends an error in the list of recorded errors or raises it, depending on the chosen
1651        error level setting.
1652        """
1653        token = token or self._curr or self._prev or Token.string("")
1654        start = token.start
1655        end = token.end + 1
1656        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1657        highlight = self.sql[start:end]
1658        end_context = self.sql[end : end + self.error_message_context]
1659
1660        error = ParseError.new(
1661            f"{message}. Line {token.line}, Col: {token.col}.\n"
1662            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1663            description=message,
1664            line=token.line,
1665            col=token.col,
1666            start_context=start_context,
1667            highlight=highlight,
1668            end_context=end_context,
1669        )
1670
1671        if self.error_level == ErrorLevel.IMMEDIATE:
1672            raise error
1673
1674        self.errors.append(error)

Appends an error in the list of recorded errors or raises it, depending on the chosen error level setting.

def expression( self, exp_class: Type[~E], comments: Optional[List[str]] = None, **kwargs) -> ~E:
1676    def expression(
1677        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1678    ) -> E:
1679        """
1680        Creates a new, validated Expression.
1681
1682        Args:
1683            exp_class: The expression class to instantiate.
1684            comments: An optional list of comments to attach to the expression.
1685            kwargs: The arguments to set for the expression along with their respective values.
1686
1687        Returns:
1688            The target expression.
1689        """
1690        instance = exp_class(**kwargs)
1691        instance.add_comments(comments) if comments else self._add_comments(instance)
1692        return self.validate_expression(instance)

Creates a new, validated Expression.

Arguments:
  • exp_class: The expression class to instantiate.
  • comments: An optional list of comments to attach to the expression.
  • kwargs: The arguments to set for the expression along with their respective values.
Returns:

The target expression.

def validate_expression(self, expression: ~E, args: Optional[List] = None) -> ~E:
1699    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1700        """
1701        Validates an Expression, making sure that all its mandatory arguments are set.
1702
1703        Args:
1704            expression: The expression to validate.
1705            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1706
1707        Returns:
1708            The validated expression.
1709        """
1710        if self.error_level != ErrorLevel.IGNORE:
1711            for error_message in expression.error_messages(args):
1712                self.raise_error(error_message)
1713
1714        return expression

Validates an Expression, making sure that all its mandatory arguments are set.

Arguments:
  • expression: The expression to validate.
  • args: An optional list of items that was used to instantiate the expression, if it's a Func.
Returns:

The validated expression.

def parse_set_operation( self, this: Optional[sqlglot.expressions.Expression]) -> Optional[sqlglot.expressions.Expression]:
4687    def parse_set_operation(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4688        start = self._index
4689        _, side_token, kind_token = self._parse_join_parts()
4690
4691        side = side_token.text if side_token else None
4692        kind = kind_token.text if kind_token else None
4693
4694        if not self._match_set(self.SET_OPERATIONS):
4695            self._retreat(start)
4696            return None
4697
4698        token_type = self._prev.token_type
4699
4700        if token_type == TokenType.UNION:
4701            operation: t.Type[exp.SetOperation] = exp.Union
4702        elif token_type == TokenType.EXCEPT:
4703            operation = exp.Except
4704        else:
4705            operation = exp.Intersect
4706
4707        comments = self._prev.comments
4708
4709        if self._match(TokenType.DISTINCT):
4710            distinct: t.Optional[bool] = True
4711        elif self._match(TokenType.ALL):
4712            distinct = False
4713        else:
4714            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4715            if distinct is None:
4716                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4717
4718        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4719            "STRICT", "CORRESPONDING"
4720        )
4721        if self._match_text_seq("CORRESPONDING"):
4722            by_name = True
4723            if not side and not kind:
4724                kind = "INNER"
4725
4726        on_column_list = None
4727        if by_name and self._match_texts(("ON", "BY")):
4728            on_column_list = self._parse_wrapped_csv(self._parse_column)
4729
4730        expression = self._parse_select(nested=True, parse_set_operation=False)
4731
4732        return self.expression(
4733            operation,
4734            comments=comments,
4735            this=this,
4736            distinct=distinct,
4737            by_name=by_name,
4738            expression=expression,
4739            side=side,
4740            kind=kind,
4741            on=on_column_list,
4742        )
errors
sql