Edit on GitHub

sqlglot.parser

   1from __future__ import annotations
   2
   3import logging
   4import re
   5import typing as t
   6import itertools
   7from collections import defaultdict
   8
   9from sqlglot import exp
  10from sqlglot.errors import ErrorLevel, ParseError, concat_messages, merge_errors
  11from sqlglot.helper import apply_index_offset, ensure_list, seq_get
  12from sqlglot.time import format_time
  13from sqlglot.tokens import Token, Tokenizer, TokenType
  14from sqlglot.trie import TrieResult, in_trie, new_trie
  15
  16if t.TYPE_CHECKING:
  17    from sqlglot._typing import E, Lit
  18    from sqlglot.dialects.dialect import Dialect, DialectType
  19
  20    T = t.TypeVar("T")
  21    TCeilFloor = t.TypeVar("TCeilFloor", exp.Ceil, exp.Floor)
  22
  23logger = logging.getLogger("sqlglot")
  24
  25OPTIONS_TYPE = t.Dict[str, t.Sequence[t.Union[t.Sequence[str], str]]]
  26
  27# Used to detect alphabetical characters and +/- in timestamp literals
  28TIME_ZONE_RE: t.Pattern[str] = re.compile(r":.*?[a-zA-Z\+\-]")
  29
  30
  31def build_var_map(args: t.List) -> exp.StarMap | exp.VarMap:
  32    if len(args) == 1 and args[0].is_star:
  33        return exp.StarMap(this=args[0])
  34
  35    keys = []
  36    values = []
  37    for i in range(0, len(args), 2):
  38        keys.append(args[i])
  39        values.append(args[i + 1])
  40
  41    return exp.VarMap(keys=exp.array(*keys, copy=False), values=exp.array(*values, copy=False))
  42
  43
  44def build_like(args: t.List) -> exp.Escape | exp.Like:
  45    like = exp.Like(this=seq_get(args, 1), expression=seq_get(args, 0))
  46    return exp.Escape(this=like, expression=seq_get(args, 2)) if len(args) > 2 else like
  47
  48
  49def binary_range_parser(
  50    expr_type: t.Type[exp.Expression], reverse_args: bool = False
  51) -> t.Callable[[Parser, t.Optional[exp.Expression]], t.Optional[exp.Expression]]:
  52    def _parse_binary_range(
  53        self: Parser, this: t.Optional[exp.Expression]
  54    ) -> t.Optional[exp.Expression]:
  55        expression = self._parse_bitwise()
  56        if reverse_args:
  57            this, expression = expression, this
  58        return self._parse_escape(self.expression(expr_type, this=this, expression=expression))
  59
  60    return _parse_binary_range
  61
  62
  63def build_logarithm(args: t.List, dialect: Dialect) -> exp.Func:
  64    # Default argument order is base, expression
  65    this = seq_get(args, 0)
  66    expression = seq_get(args, 1)
  67
  68    if expression:
  69        if not dialect.LOG_BASE_FIRST:
  70            this, expression = expression, this
  71        return exp.Log(this=this, expression=expression)
  72
  73    return (exp.Ln if dialect.parser_class.LOG_DEFAULTS_TO_LN else exp.Log)(this=this)
  74
  75
  76def build_hex(args: t.List, dialect: Dialect) -> exp.Hex | exp.LowerHex:
  77    arg = seq_get(args, 0)
  78    return exp.LowerHex(this=arg) if dialect.HEX_LOWERCASE else exp.Hex(this=arg)
  79
  80
  81def build_lower(args: t.List) -> exp.Lower | exp.Hex:
  82    # LOWER(HEX(..)) can be simplified to LowerHex to simplify its transpilation
  83    arg = seq_get(args, 0)
  84    return exp.LowerHex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Lower(this=arg)
  85
  86
  87def build_upper(args: t.List) -> exp.Upper | exp.Hex:
  88    # UPPER(HEX(..)) can be simplified to Hex to simplify its transpilation
  89    arg = seq_get(args, 0)
  90    return exp.Hex(this=arg.this) if isinstance(arg, exp.Hex) else exp.Upper(this=arg)
  91
  92
  93def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
  94    def _builder(args: t.List, dialect: Dialect) -> E:
  95        expression = expr_type(
  96            this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
  97        )
  98        if len(args) > 2 and expr_type is exp.JSONExtract:
  99            expression.set("expressions", args[2:])
 100
 101        return expression
 102
 103    return _builder
 104
 105
 106def build_mod(args: t.List) -> exp.Mod:
 107    this = seq_get(args, 0)
 108    expression = seq_get(args, 1)
 109
 110    # Wrap the operands if they are binary nodes, e.g. MOD(a + 1, 7) -> (a + 1) % 7
 111    this = exp.Paren(this=this) if isinstance(this, exp.Binary) else this
 112    expression = exp.Paren(this=expression) if isinstance(expression, exp.Binary) else expression
 113
 114    return exp.Mod(this=this, expression=expression)
 115
 116
 117def build_pad(args: t.List, is_left: bool = True):
 118    return exp.Pad(
 119        this=seq_get(args, 0),
 120        expression=seq_get(args, 1),
 121        fill_pattern=seq_get(args, 2),
 122        is_left=is_left,
 123    )
 124
 125
 126def build_array_constructor(
 127    exp_class: t.Type[E], args: t.List, bracket_kind: TokenType, dialect: Dialect
 128) -> exp.Expression:
 129    array_exp = exp_class(expressions=args)
 130
 131    if exp_class == exp.Array and dialect.HAS_DISTINCT_ARRAY_CONSTRUCTORS:
 132        array_exp.set("bracket_notation", bracket_kind == TokenType.L_BRACKET)
 133
 134    return array_exp
 135
 136
 137def build_convert_timezone(
 138    args: t.List, default_source_tz: t.Optional[str] = None
 139) -> t.Union[exp.ConvertTimezone, exp.Anonymous]:
 140    if len(args) == 2:
 141        source_tz = exp.Literal.string(default_source_tz) if default_source_tz else None
 142        return exp.ConvertTimezone(
 143            source_tz=source_tz, target_tz=seq_get(args, 0), timestamp=seq_get(args, 1)
 144        )
 145
 146    return exp.ConvertTimezone.from_arg_list(args)
 147
 148
 149def build_trim(args: t.List, is_left: bool = True):
 150    return exp.Trim(
 151        this=seq_get(args, 0),
 152        expression=seq_get(args, 1),
 153        position="LEADING" if is_left else "TRAILING",
 154    )
 155
 156
 157def build_coalesce(
 158    args: t.List, is_nvl: t.Optional[bool] = None, is_null: t.Optional[bool] = None
 159) -> exp.Coalesce:
 160    return exp.Coalesce(this=seq_get(args, 0), expressions=args[1:], is_nvl=is_nvl, is_null=is_null)
 161
 162
 163def build_locate_strposition(args: t.List):
 164    return exp.StrPosition(
 165        this=seq_get(args, 1),
 166        substr=seq_get(args, 0),
 167        position=seq_get(args, 2),
 168    )
 169
 170
 171class _Parser(type):
 172    def __new__(cls, clsname, bases, attrs):
 173        klass = super().__new__(cls, clsname, bases, attrs)
 174
 175        klass.SHOW_TRIE = new_trie(key.split(" ") for key in klass.SHOW_PARSERS)
 176        klass.SET_TRIE = new_trie(key.split(" ") for key in klass.SET_PARSERS)
 177
 178        return klass
 179
 180
 181class Parser(metaclass=_Parser):
 182    """
 183    Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.
 184
 185    Args:
 186        error_level: The desired error level.
 187            Default: ErrorLevel.IMMEDIATE
 188        error_message_context: The amount of context to capture from a query string when displaying
 189            the error message (in number of characters).
 190            Default: 100
 191        max_errors: Maximum number of error messages to include in a raised ParseError.
 192            This is only relevant if error_level is ErrorLevel.RAISE.
 193            Default: 3
 194    """
 195
 196    FUNCTIONS: t.Dict[str, t.Callable] = {
 197        **{name: func.from_arg_list for name, func in exp.FUNCTION_BY_NAME.items()},
 198        **dict.fromkeys(("COALESCE", "IFNULL", "NVL"), build_coalesce),
 199        "ARRAY": lambda args, dialect: exp.Array(expressions=args),
 200        "ARRAYAGG": lambda args, dialect: exp.ArrayAgg(
 201            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 202        ),
 203        "ARRAY_AGG": lambda args, dialect: exp.ArrayAgg(
 204            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 205        ),
 206        "CHAR": lambda args: exp.Chr(expressions=args),
 207        "CHR": lambda args: exp.Chr(expressions=args),
 208        "COUNT": lambda args: exp.Count(this=seq_get(args, 0), expressions=args[1:], big_int=True),
 209        "CONCAT": lambda args, dialect: exp.Concat(
 210            expressions=args,
 211            safe=not dialect.STRICT_STRING_CONCAT,
 212            coalesce=dialect.CONCAT_COALESCE,
 213        ),
 214        "CONCAT_WS": lambda args, dialect: exp.ConcatWs(
 215            expressions=args,
 216            safe=not dialect.STRICT_STRING_CONCAT,
 217            coalesce=dialect.CONCAT_COALESCE,
 218        ),
 219        "CONVERT_TIMEZONE": build_convert_timezone,
 220        "DATE_TO_DATE_STR": lambda args: exp.Cast(
 221            this=seq_get(args, 0),
 222            to=exp.DataType(this=exp.DataType.Type.TEXT),
 223        ),
 224        "GENERATE_DATE_ARRAY": lambda args: exp.GenerateDateArray(
 225            start=seq_get(args, 0),
 226            end=seq_get(args, 1),
 227            step=seq_get(args, 2) or exp.Interval(this=exp.Literal.string(1), unit=exp.var("DAY")),
 228        ),
 229        "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)),
 230        "HEX": build_hex,
 231        "JSON_EXTRACT": build_extract_json_with_path(exp.JSONExtract),
 232        "JSON_EXTRACT_SCALAR": build_extract_json_with_path(exp.JSONExtractScalar),
 233        "JSON_EXTRACT_PATH_TEXT": build_extract_json_with_path(exp.JSONExtractScalar),
 234        "LIKE": build_like,
 235        "LOG": build_logarithm,
 236        "LOG2": lambda args: exp.Log(this=exp.Literal.number(2), expression=seq_get(args, 0)),
 237        "LOG10": lambda args: exp.Log(this=exp.Literal.number(10), expression=seq_get(args, 0)),
 238        "LOWER": build_lower,
 239        "LPAD": lambda args: build_pad(args),
 240        "LEFTPAD": lambda args: build_pad(args),
 241        "LTRIM": lambda args: build_trim(args),
 242        "MOD": build_mod,
 243        "RIGHTPAD": lambda args: build_pad(args, is_left=False),
 244        "RPAD": lambda args: build_pad(args, is_left=False),
 245        "RTRIM": lambda args: build_trim(args, is_left=False),
 246        "SCOPE_RESOLUTION": lambda args: exp.ScopeResolution(expression=seq_get(args, 0))
 247        if len(args) != 2
 248        else exp.ScopeResolution(this=seq_get(args, 0), expression=seq_get(args, 1)),
 249        "STRPOS": exp.StrPosition.from_arg_list,
 250        "CHARINDEX": lambda args: build_locate_strposition(args),
 251        "INSTR": exp.StrPosition.from_arg_list,
 252        "LOCATE": lambda args: build_locate_strposition(args),
 253        "TIME_TO_TIME_STR": lambda args: exp.Cast(
 254            this=seq_get(args, 0),
 255            to=exp.DataType(this=exp.DataType.Type.TEXT),
 256        ),
 257        "TO_HEX": build_hex,
 258        "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring(
 259            this=exp.Cast(
 260                this=seq_get(args, 0),
 261                to=exp.DataType(this=exp.DataType.Type.TEXT),
 262            ),
 263            start=exp.Literal.number(1),
 264            length=exp.Literal.number(10),
 265        ),
 266        "UNNEST": lambda args: exp.Unnest(expressions=ensure_list(seq_get(args, 0))),
 267        "UPPER": build_upper,
 268        "VAR_MAP": build_var_map,
 269    }
 270
 271    NO_PAREN_FUNCTIONS = {
 272        TokenType.CURRENT_DATE: exp.CurrentDate,
 273        TokenType.CURRENT_DATETIME: exp.CurrentDate,
 274        TokenType.CURRENT_TIME: exp.CurrentTime,
 275        TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp,
 276        TokenType.CURRENT_USER: exp.CurrentUser,
 277    }
 278
 279    STRUCT_TYPE_TOKENS = {
 280        TokenType.NESTED,
 281        TokenType.OBJECT,
 282        TokenType.STRUCT,
 283        TokenType.UNION,
 284    }
 285
 286    NESTED_TYPE_TOKENS = {
 287        TokenType.ARRAY,
 288        TokenType.LIST,
 289        TokenType.LOWCARDINALITY,
 290        TokenType.MAP,
 291        TokenType.NULLABLE,
 292        TokenType.RANGE,
 293        *STRUCT_TYPE_TOKENS,
 294    }
 295
 296    ENUM_TYPE_TOKENS = {
 297        TokenType.DYNAMIC,
 298        TokenType.ENUM,
 299        TokenType.ENUM8,
 300        TokenType.ENUM16,
 301    }
 302
 303    AGGREGATE_TYPE_TOKENS = {
 304        TokenType.AGGREGATEFUNCTION,
 305        TokenType.SIMPLEAGGREGATEFUNCTION,
 306    }
 307
 308    TYPE_TOKENS = {
 309        TokenType.BIT,
 310        TokenType.BOOLEAN,
 311        TokenType.TINYINT,
 312        TokenType.UTINYINT,
 313        TokenType.SMALLINT,
 314        TokenType.USMALLINT,
 315        TokenType.INT,
 316        TokenType.UINT,
 317        TokenType.BIGINT,
 318        TokenType.UBIGINT,
 319        TokenType.INT128,
 320        TokenType.UINT128,
 321        TokenType.INT256,
 322        TokenType.UINT256,
 323        TokenType.MEDIUMINT,
 324        TokenType.UMEDIUMINT,
 325        TokenType.FIXEDSTRING,
 326        TokenType.FLOAT,
 327        TokenType.DOUBLE,
 328        TokenType.UDOUBLE,
 329        TokenType.CHAR,
 330        TokenType.NCHAR,
 331        TokenType.VARCHAR,
 332        TokenType.NVARCHAR,
 333        TokenType.BPCHAR,
 334        TokenType.TEXT,
 335        TokenType.MEDIUMTEXT,
 336        TokenType.LONGTEXT,
 337        TokenType.BLOB,
 338        TokenType.MEDIUMBLOB,
 339        TokenType.LONGBLOB,
 340        TokenType.BINARY,
 341        TokenType.VARBINARY,
 342        TokenType.JSON,
 343        TokenType.JSONB,
 344        TokenType.INTERVAL,
 345        TokenType.TINYBLOB,
 346        TokenType.TINYTEXT,
 347        TokenType.TIME,
 348        TokenType.TIMETZ,
 349        TokenType.TIMESTAMP,
 350        TokenType.TIMESTAMP_S,
 351        TokenType.TIMESTAMP_MS,
 352        TokenType.TIMESTAMP_NS,
 353        TokenType.TIMESTAMPTZ,
 354        TokenType.TIMESTAMPLTZ,
 355        TokenType.TIMESTAMPNTZ,
 356        TokenType.DATETIME,
 357        TokenType.DATETIME2,
 358        TokenType.DATETIME64,
 359        TokenType.SMALLDATETIME,
 360        TokenType.DATE,
 361        TokenType.DATE32,
 362        TokenType.INT4RANGE,
 363        TokenType.INT4MULTIRANGE,
 364        TokenType.INT8RANGE,
 365        TokenType.INT8MULTIRANGE,
 366        TokenType.NUMRANGE,
 367        TokenType.NUMMULTIRANGE,
 368        TokenType.TSRANGE,
 369        TokenType.TSMULTIRANGE,
 370        TokenType.TSTZRANGE,
 371        TokenType.TSTZMULTIRANGE,
 372        TokenType.DATERANGE,
 373        TokenType.DATEMULTIRANGE,
 374        TokenType.DECIMAL,
 375        TokenType.DECIMAL32,
 376        TokenType.DECIMAL64,
 377        TokenType.DECIMAL128,
 378        TokenType.DECIMAL256,
 379        TokenType.UDECIMAL,
 380        TokenType.BIGDECIMAL,
 381        TokenType.UUID,
 382        TokenType.GEOGRAPHY,
 383        TokenType.GEOMETRY,
 384        TokenType.POINT,
 385        TokenType.RING,
 386        TokenType.LINESTRING,
 387        TokenType.MULTILINESTRING,
 388        TokenType.POLYGON,
 389        TokenType.MULTIPOLYGON,
 390        TokenType.HLLSKETCH,
 391        TokenType.HSTORE,
 392        TokenType.PSEUDO_TYPE,
 393        TokenType.SUPER,
 394        TokenType.SERIAL,
 395        TokenType.SMALLSERIAL,
 396        TokenType.BIGSERIAL,
 397        TokenType.XML,
 398        TokenType.YEAR,
 399        TokenType.USERDEFINED,
 400        TokenType.MONEY,
 401        TokenType.SMALLMONEY,
 402        TokenType.ROWVERSION,
 403        TokenType.IMAGE,
 404        TokenType.VARIANT,
 405        TokenType.VECTOR,
 406        TokenType.VOID,
 407        TokenType.OBJECT,
 408        TokenType.OBJECT_IDENTIFIER,
 409        TokenType.INET,
 410        TokenType.IPADDRESS,
 411        TokenType.IPPREFIX,
 412        TokenType.IPV4,
 413        TokenType.IPV6,
 414        TokenType.UNKNOWN,
 415        TokenType.NOTHING,
 416        TokenType.NULL,
 417        TokenType.NAME,
 418        TokenType.TDIGEST,
 419        TokenType.DYNAMIC,
 420        *ENUM_TYPE_TOKENS,
 421        *NESTED_TYPE_TOKENS,
 422        *AGGREGATE_TYPE_TOKENS,
 423    }
 424
 425    SIGNED_TO_UNSIGNED_TYPE_TOKEN = {
 426        TokenType.BIGINT: TokenType.UBIGINT,
 427        TokenType.INT: TokenType.UINT,
 428        TokenType.MEDIUMINT: TokenType.UMEDIUMINT,
 429        TokenType.SMALLINT: TokenType.USMALLINT,
 430        TokenType.TINYINT: TokenType.UTINYINT,
 431        TokenType.DECIMAL: TokenType.UDECIMAL,
 432        TokenType.DOUBLE: TokenType.UDOUBLE,
 433    }
 434
 435    SUBQUERY_PREDICATES = {
 436        TokenType.ANY: exp.Any,
 437        TokenType.ALL: exp.All,
 438        TokenType.EXISTS: exp.Exists,
 439        TokenType.SOME: exp.Any,
 440    }
 441
 442    RESERVED_TOKENS = {
 443        *Tokenizer.SINGLE_TOKENS.values(),
 444        TokenType.SELECT,
 445    } - {TokenType.IDENTIFIER}
 446
 447    DB_CREATABLES = {
 448        TokenType.DATABASE,
 449        TokenType.DICTIONARY,
 450        TokenType.FILE_FORMAT,
 451        TokenType.MODEL,
 452        TokenType.NAMESPACE,
 453        TokenType.SCHEMA,
 454        TokenType.SEQUENCE,
 455        TokenType.SINK,
 456        TokenType.SOURCE,
 457        TokenType.STAGE,
 458        TokenType.STORAGE_INTEGRATION,
 459        TokenType.STREAMLIT,
 460        TokenType.TABLE,
 461        TokenType.TAG,
 462        TokenType.VIEW,
 463        TokenType.WAREHOUSE,
 464    }
 465
 466    CREATABLES = {
 467        TokenType.COLUMN,
 468        TokenType.CONSTRAINT,
 469        TokenType.FOREIGN_KEY,
 470        TokenType.FUNCTION,
 471        TokenType.INDEX,
 472        TokenType.PROCEDURE,
 473        *DB_CREATABLES,
 474    }
 475
 476    ALTERABLES = {
 477        TokenType.INDEX,
 478        TokenType.TABLE,
 479        TokenType.VIEW,
 480    }
 481
 482    # Tokens that can represent identifiers
 483    ID_VAR_TOKENS = {
 484        TokenType.ALL,
 485        TokenType.ATTACH,
 486        TokenType.VAR,
 487        TokenType.ANTI,
 488        TokenType.APPLY,
 489        TokenType.ASC,
 490        TokenType.ASOF,
 491        TokenType.AUTO_INCREMENT,
 492        TokenType.BEGIN,
 493        TokenType.BPCHAR,
 494        TokenType.CACHE,
 495        TokenType.CASE,
 496        TokenType.COLLATE,
 497        TokenType.COMMAND,
 498        TokenType.COMMENT,
 499        TokenType.COMMIT,
 500        TokenType.CONSTRAINT,
 501        TokenType.COPY,
 502        TokenType.CUBE,
 503        TokenType.CURRENT_SCHEMA,
 504        TokenType.DEFAULT,
 505        TokenType.DELETE,
 506        TokenType.DESC,
 507        TokenType.DESCRIBE,
 508        TokenType.DETACH,
 509        TokenType.DICTIONARY,
 510        TokenType.DIV,
 511        TokenType.END,
 512        TokenType.EXECUTE,
 513        TokenType.EXPORT,
 514        TokenType.ESCAPE,
 515        TokenType.FALSE,
 516        TokenType.FIRST,
 517        TokenType.FILTER,
 518        TokenType.FINAL,
 519        TokenType.FORMAT,
 520        TokenType.FULL,
 521        TokenType.GET,
 522        TokenType.IDENTIFIER,
 523        TokenType.IS,
 524        TokenType.ISNULL,
 525        TokenType.INTERVAL,
 526        TokenType.KEEP,
 527        TokenType.KILL,
 528        TokenType.LEFT,
 529        TokenType.LIMIT,
 530        TokenType.LOAD,
 531        TokenType.MERGE,
 532        TokenType.NATURAL,
 533        TokenType.NEXT,
 534        TokenType.OFFSET,
 535        TokenType.OPERATOR,
 536        TokenType.ORDINALITY,
 537        TokenType.OVERLAPS,
 538        TokenType.OVERWRITE,
 539        TokenType.PARTITION,
 540        TokenType.PERCENT,
 541        TokenType.PIVOT,
 542        TokenType.PRAGMA,
 543        TokenType.PUT,
 544        TokenType.RANGE,
 545        TokenType.RECURSIVE,
 546        TokenType.REFERENCES,
 547        TokenType.REFRESH,
 548        TokenType.RENAME,
 549        TokenType.REPLACE,
 550        TokenType.RIGHT,
 551        TokenType.ROLLUP,
 552        TokenType.ROW,
 553        TokenType.ROWS,
 554        TokenType.SEMI,
 555        TokenType.SET,
 556        TokenType.SETTINGS,
 557        TokenType.SHOW,
 558        TokenType.TEMPORARY,
 559        TokenType.TOP,
 560        TokenType.TRUE,
 561        TokenType.TRUNCATE,
 562        TokenType.UNIQUE,
 563        TokenType.UNNEST,
 564        TokenType.UNPIVOT,
 565        TokenType.UPDATE,
 566        TokenType.USE,
 567        TokenType.VOLATILE,
 568        TokenType.WINDOW,
 569        *CREATABLES,
 570        *SUBQUERY_PREDICATES,
 571        *TYPE_TOKENS,
 572        *NO_PAREN_FUNCTIONS,
 573    }
 574    ID_VAR_TOKENS.remove(TokenType.UNION)
 575
 576    TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
 577        TokenType.ANTI,
 578        TokenType.APPLY,
 579        TokenType.ASOF,
 580        TokenType.FULL,
 581        TokenType.LEFT,
 582        TokenType.LOCK,
 583        TokenType.NATURAL,
 584        TokenType.RIGHT,
 585        TokenType.SEMI,
 586        TokenType.WINDOW,
 587    }
 588
 589    ALIAS_TOKENS = ID_VAR_TOKENS
 590
 591    COLON_PLACEHOLDER_TOKENS = ID_VAR_TOKENS
 592
 593    ARRAY_CONSTRUCTORS = {
 594        "ARRAY": exp.Array,
 595        "LIST": exp.List,
 596    }
 597
 598    COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS}
 599
 600    UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET}
 601
 602    TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"}
 603
 604    FUNC_TOKENS = {
 605        TokenType.COLLATE,
 606        TokenType.COMMAND,
 607        TokenType.CURRENT_DATE,
 608        TokenType.CURRENT_DATETIME,
 609        TokenType.CURRENT_SCHEMA,
 610        TokenType.CURRENT_TIMESTAMP,
 611        TokenType.CURRENT_TIME,
 612        TokenType.CURRENT_USER,
 613        TokenType.FILTER,
 614        TokenType.FIRST,
 615        TokenType.FORMAT,
 616        TokenType.GET,
 617        TokenType.GLOB,
 618        TokenType.IDENTIFIER,
 619        TokenType.INDEX,
 620        TokenType.ISNULL,
 621        TokenType.ILIKE,
 622        TokenType.INSERT,
 623        TokenType.LIKE,
 624        TokenType.MERGE,
 625        TokenType.NEXT,
 626        TokenType.OFFSET,
 627        TokenType.PRIMARY_KEY,
 628        TokenType.RANGE,
 629        TokenType.REPLACE,
 630        TokenType.RLIKE,
 631        TokenType.ROW,
 632        TokenType.UNNEST,
 633        TokenType.VAR,
 634        TokenType.LEFT,
 635        TokenType.RIGHT,
 636        TokenType.SEQUENCE,
 637        TokenType.DATE,
 638        TokenType.DATETIME,
 639        TokenType.TABLE,
 640        TokenType.TIMESTAMP,
 641        TokenType.TIMESTAMPTZ,
 642        TokenType.TRUNCATE,
 643        TokenType.WINDOW,
 644        TokenType.XOR,
 645        *TYPE_TOKENS,
 646        *SUBQUERY_PREDICATES,
 647    }
 648
 649    CONJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 650        TokenType.AND: exp.And,
 651    }
 652
 653    ASSIGNMENT: t.Dict[TokenType, t.Type[exp.Expression]] = {
 654        TokenType.COLON_EQ: exp.PropertyEQ,
 655    }
 656
 657    DISJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 658        TokenType.OR: exp.Or,
 659    }
 660
 661    EQUALITY = {
 662        TokenType.EQ: exp.EQ,
 663        TokenType.NEQ: exp.NEQ,
 664        TokenType.NULLSAFE_EQ: exp.NullSafeEQ,
 665    }
 666
 667    COMPARISON = {
 668        TokenType.GT: exp.GT,
 669        TokenType.GTE: exp.GTE,
 670        TokenType.LT: exp.LT,
 671        TokenType.LTE: exp.LTE,
 672    }
 673
 674    BITWISE = {
 675        TokenType.AMP: exp.BitwiseAnd,
 676        TokenType.CARET: exp.BitwiseXor,
 677        TokenType.PIPE: exp.BitwiseOr,
 678    }
 679
 680    TERM = {
 681        TokenType.DASH: exp.Sub,
 682        TokenType.PLUS: exp.Add,
 683        TokenType.MOD: exp.Mod,
 684        TokenType.COLLATE: exp.Collate,
 685    }
 686
 687    FACTOR = {
 688        TokenType.DIV: exp.IntDiv,
 689        TokenType.LR_ARROW: exp.Distance,
 690        TokenType.SLASH: exp.Div,
 691        TokenType.STAR: exp.Mul,
 692    }
 693
 694    EXPONENT: t.Dict[TokenType, t.Type[exp.Expression]] = {}
 695
 696    TIMES = {
 697        TokenType.TIME,
 698        TokenType.TIMETZ,
 699    }
 700
 701    TIMESTAMPS = {
 702        TokenType.TIMESTAMP,
 703        TokenType.TIMESTAMPNTZ,
 704        TokenType.TIMESTAMPTZ,
 705        TokenType.TIMESTAMPLTZ,
 706        *TIMES,
 707    }
 708
 709    SET_OPERATIONS = {
 710        TokenType.UNION,
 711        TokenType.INTERSECT,
 712        TokenType.EXCEPT,
 713    }
 714
 715    JOIN_METHODS = {
 716        TokenType.ASOF,
 717        TokenType.NATURAL,
 718        TokenType.POSITIONAL,
 719    }
 720
 721    JOIN_SIDES = {
 722        TokenType.LEFT,
 723        TokenType.RIGHT,
 724        TokenType.FULL,
 725    }
 726
 727    JOIN_KINDS = {
 728        TokenType.ANTI,
 729        TokenType.CROSS,
 730        TokenType.INNER,
 731        TokenType.OUTER,
 732        TokenType.SEMI,
 733        TokenType.STRAIGHT_JOIN,
 734    }
 735
 736    JOIN_HINTS: t.Set[str] = set()
 737
 738    LAMBDAS = {
 739        TokenType.ARROW: lambda self, expressions: self.expression(
 740            exp.Lambda,
 741            this=self._replace_lambda(
 742                self._parse_assignment(),
 743                expressions,
 744            ),
 745            expressions=expressions,
 746        ),
 747        TokenType.FARROW: lambda self, expressions: self.expression(
 748            exp.Kwarg,
 749            this=exp.var(expressions[0].name),
 750            expression=self._parse_assignment(),
 751        ),
 752    }
 753
 754    COLUMN_OPERATORS = {
 755        TokenType.DOT: None,
 756        TokenType.DOTCOLON: lambda self, this, to: self.expression(
 757            exp.JSONCast,
 758            this=this,
 759            to=to,
 760        ),
 761        TokenType.DCOLON: lambda self, this, to: self.expression(
 762            exp.Cast if self.STRICT_CAST else exp.TryCast,
 763            this=this,
 764            to=to,
 765        ),
 766        TokenType.ARROW: lambda self, this, path: self.expression(
 767            exp.JSONExtract,
 768            this=this,
 769            expression=self.dialect.to_json_path(path),
 770            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 771        ),
 772        TokenType.DARROW: lambda self, this, path: self.expression(
 773            exp.JSONExtractScalar,
 774            this=this,
 775            expression=self.dialect.to_json_path(path),
 776            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 777        ),
 778        TokenType.HASH_ARROW: lambda self, this, path: self.expression(
 779            exp.JSONBExtract,
 780            this=this,
 781            expression=path,
 782        ),
 783        TokenType.DHASH_ARROW: lambda self, this, path: self.expression(
 784            exp.JSONBExtractScalar,
 785            this=this,
 786            expression=path,
 787        ),
 788        TokenType.PLACEHOLDER: lambda self, this, key: self.expression(
 789            exp.JSONBContains,
 790            this=this,
 791            expression=key,
 792        ),
 793    }
 794
 795    EXPRESSION_PARSERS = {
 796        exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
 797        exp.Column: lambda self: self._parse_column(),
 798        exp.Condition: lambda self: self._parse_assignment(),
 799        exp.DataType: lambda self: self._parse_types(allow_identifiers=False, schema=True),
 800        exp.Expression: lambda self: self._parse_expression(),
 801        exp.From: lambda self: self._parse_from(joins=True),
 802        exp.Group: lambda self: self._parse_group(),
 803        exp.Having: lambda self: self._parse_having(),
 804        exp.Hint: lambda self: self._parse_hint_body(),
 805        exp.Identifier: lambda self: self._parse_id_var(),
 806        exp.Join: lambda self: self._parse_join(),
 807        exp.Lambda: lambda self: self._parse_lambda(),
 808        exp.Lateral: lambda self: self._parse_lateral(),
 809        exp.Limit: lambda self: self._parse_limit(),
 810        exp.Offset: lambda self: self._parse_offset(),
 811        exp.Order: lambda self: self._parse_order(),
 812        exp.Ordered: lambda self: self._parse_ordered(),
 813        exp.Properties: lambda self: self._parse_properties(),
 814        exp.PartitionedByProperty: lambda self: self._parse_partitioned_by(),
 815        exp.Qualify: lambda self: self._parse_qualify(),
 816        exp.Returning: lambda self: self._parse_returning(),
 817        exp.Select: lambda self: self._parse_select(),
 818        exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY),
 819        exp.Table: lambda self: self._parse_table_parts(),
 820        exp.TableAlias: lambda self: self._parse_table_alias(),
 821        exp.Tuple: lambda self: self._parse_value(values=False),
 822        exp.Whens: lambda self: self._parse_when_matched(),
 823        exp.Where: lambda self: self._parse_where(),
 824        exp.Window: lambda self: self._parse_named_window(),
 825        exp.With: lambda self: self._parse_with(),
 826        "JOIN_TYPE": lambda self: self._parse_join_parts(),
 827    }
 828
 829    STATEMENT_PARSERS = {
 830        TokenType.ALTER: lambda self: self._parse_alter(),
 831        TokenType.ANALYZE: lambda self: self._parse_analyze(),
 832        TokenType.BEGIN: lambda self: self._parse_transaction(),
 833        TokenType.CACHE: lambda self: self._parse_cache(),
 834        TokenType.COMMENT: lambda self: self._parse_comment(),
 835        TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(),
 836        TokenType.COPY: lambda self: self._parse_copy(),
 837        TokenType.CREATE: lambda self: self._parse_create(),
 838        TokenType.DELETE: lambda self: self._parse_delete(),
 839        TokenType.DESC: lambda self: self._parse_describe(),
 840        TokenType.DESCRIBE: lambda self: self._parse_describe(),
 841        TokenType.DROP: lambda self: self._parse_drop(),
 842        TokenType.GRANT: lambda self: self._parse_grant(),
 843        TokenType.INSERT: lambda self: self._parse_insert(),
 844        TokenType.KILL: lambda self: self._parse_kill(),
 845        TokenType.LOAD: lambda self: self._parse_load(),
 846        TokenType.MERGE: lambda self: self._parse_merge(),
 847        TokenType.PIVOT: lambda self: self._parse_simplified_pivot(),
 848        TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()),
 849        TokenType.REFRESH: lambda self: self._parse_refresh(),
 850        TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(),
 851        TokenType.SET: lambda self: self._parse_set(),
 852        TokenType.TRUNCATE: lambda self: self._parse_truncate_table(),
 853        TokenType.UNCACHE: lambda self: self._parse_uncache(),
 854        TokenType.UNPIVOT: lambda self: self._parse_simplified_pivot(is_unpivot=True),
 855        TokenType.UPDATE: lambda self: self._parse_update(),
 856        TokenType.USE: lambda self: self._parse_use(),
 857        TokenType.SEMICOLON: lambda self: exp.Semicolon(),
 858    }
 859
 860    UNARY_PARSERS = {
 861        TokenType.PLUS: lambda self: self._parse_unary(),  # Unary + is handled as a no-op
 862        TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()),
 863        TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()),
 864        TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()),
 865        TokenType.PIPE_SLASH: lambda self: self.expression(exp.Sqrt, this=self._parse_unary()),
 866        TokenType.DPIPE_SLASH: lambda self: self.expression(exp.Cbrt, this=self._parse_unary()),
 867    }
 868
 869    STRING_PARSERS = {
 870        TokenType.HEREDOC_STRING: lambda self, token: self.expression(
 871            exp.RawString, this=token.text
 872        ),
 873        TokenType.NATIONAL_STRING: lambda self, token: self.expression(
 874            exp.National, this=token.text
 875        ),
 876        TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text),
 877        TokenType.STRING: lambda self, token: self.expression(
 878            exp.Literal, this=token.text, is_string=True
 879        ),
 880        TokenType.UNICODE_STRING: lambda self, token: self.expression(
 881            exp.UnicodeString,
 882            this=token.text,
 883            escape=self._match_text_seq("UESCAPE") and self._parse_string(),
 884        ),
 885    }
 886
 887    NUMERIC_PARSERS = {
 888        TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text),
 889        TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text),
 890        TokenType.HEX_STRING: lambda self, token: self.expression(
 891            exp.HexString,
 892            this=token.text,
 893            is_integer=self.dialect.HEX_STRING_IS_INTEGER_TYPE or None,
 894        ),
 895        TokenType.NUMBER: lambda self, token: self.expression(
 896            exp.Literal, this=token.text, is_string=False
 897        ),
 898    }
 899
 900    PRIMARY_PARSERS = {
 901        **STRING_PARSERS,
 902        **NUMERIC_PARSERS,
 903        TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token),
 904        TokenType.NULL: lambda self, _: self.expression(exp.Null),
 905        TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True),
 906        TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False),
 907        TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(),
 908        TokenType.STAR: lambda self, _: self._parse_star_ops(),
 909    }
 910
 911    PLACEHOLDER_PARSERS = {
 912        TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
 913        TokenType.PARAMETER: lambda self: self._parse_parameter(),
 914        TokenType.COLON: lambda self: (
 915            self.expression(exp.Placeholder, this=self._prev.text)
 916            if self._match_set(self.COLON_PLACEHOLDER_TOKENS)
 917            else None
 918        ),
 919    }
 920
 921    RANGE_PARSERS = {
 922        TokenType.AT_GT: binary_range_parser(exp.ArrayContainsAll),
 923        TokenType.BETWEEN: lambda self, this: self._parse_between(this),
 924        TokenType.GLOB: binary_range_parser(exp.Glob),
 925        TokenType.ILIKE: binary_range_parser(exp.ILike),
 926        TokenType.IN: lambda self, this: self._parse_in(this),
 927        TokenType.IRLIKE: binary_range_parser(exp.RegexpILike),
 928        TokenType.IS: lambda self, this: self._parse_is(this),
 929        TokenType.LIKE: binary_range_parser(exp.Like),
 930        TokenType.LT_AT: binary_range_parser(exp.ArrayContainsAll, reverse_args=True),
 931        TokenType.OVERLAPS: binary_range_parser(exp.Overlaps),
 932        TokenType.RLIKE: binary_range_parser(exp.RegexpLike),
 933        TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo),
 934        TokenType.FOR: lambda self, this: self._parse_comprehension(this),
 935    }
 936
 937    PIPE_SYNTAX_TRANSFORM_PARSERS = {
 938        "AGGREGATE": lambda self, query: self._parse_pipe_syntax_aggregate(query),
 939        "AS": lambda self, query: self._build_pipe_cte(
 940            query, [exp.Star()], self._parse_table_alias()
 941        ),
 942        "EXTEND": lambda self, query: self._parse_pipe_syntax_extend(query),
 943        "LIMIT": lambda self, query: self._parse_pipe_syntax_limit(query),
 944        "ORDER BY": lambda self, query: query.order_by(
 945            self._parse_order(), append=False, copy=False
 946        ),
 947        "PIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
 948        "SELECT": lambda self, query: self._parse_pipe_syntax_select(query),
 949        "TABLESAMPLE": lambda self, query: self._parse_pipe_syntax_tablesample(query),
 950        "UNPIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
 951        "WHERE": lambda self, query: query.where(self._parse_where(), copy=False),
 952    }
 953
 954    PROPERTY_PARSERS: t.Dict[str, t.Callable] = {
 955        "ALLOWED_VALUES": lambda self: self.expression(
 956            exp.AllowedValuesProperty, expressions=self._parse_csv(self._parse_primary)
 957        ),
 958        "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty),
 959        "AUTO": lambda self: self._parse_auto_property(),
 960        "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty),
 961        "BACKUP": lambda self: self.expression(
 962            exp.BackupProperty, this=self._parse_var(any_token=True)
 963        ),
 964        "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(),
 965        "CHARSET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 966        "CHARACTER SET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 967        "CHECKSUM": lambda self: self._parse_checksum(),
 968        "CLUSTER BY": lambda self: self._parse_cluster(),
 969        "CLUSTERED": lambda self: self._parse_clustered_by(),
 970        "COLLATE": lambda self, **kwargs: self._parse_property_assignment(
 971            exp.CollateProperty, **kwargs
 972        ),
 973        "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty),
 974        "CONTAINS": lambda self: self._parse_contains_property(),
 975        "COPY": lambda self: self._parse_copy_property(),
 976        "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs),
 977        "DATA_DELETION": lambda self: self._parse_data_deletion_property(),
 978        "DEFINER": lambda self: self._parse_definer(),
 979        "DETERMINISTIC": lambda self: self.expression(
 980            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 981        ),
 982        "DISTRIBUTED": lambda self: self._parse_distributed_property(),
 983        "DUPLICATE": lambda self: self._parse_composite_key_property(exp.DuplicateKeyProperty),
 984        "DYNAMIC": lambda self: self.expression(exp.DynamicProperty),
 985        "DISTKEY": lambda self: self._parse_distkey(),
 986        "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty),
 987        "EMPTY": lambda self: self.expression(exp.EmptyProperty),
 988        "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty),
 989        "ENVIRONMENT": lambda self: self.expression(
 990            exp.EnviromentProperty, expressions=self._parse_wrapped_csv(self._parse_assignment)
 991        ),
 992        "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty),
 993        "EXTERNAL": lambda self: self.expression(exp.ExternalProperty),
 994        "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs),
 995        "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
 996        "FREESPACE": lambda self: self._parse_freespace(),
 997        "GLOBAL": lambda self: self.expression(exp.GlobalProperty),
 998        "HEAP": lambda self: self.expression(exp.HeapProperty),
 999        "ICEBERG": lambda self: self.expression(exp.IcebergProperty),
1000        "IMMUTABLE": lambda self: self.expression(
1001            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
1002        ),
1003        "INHERITS": lambda self: self.expression(
1004            exp.InheritsProperty, expressions=self._parse_wrapped_csv(self._parse_table)
1005        ),
1006        "INPUT": lambda self: self.expression(exp.InputModelProperty, this=self._parse_schema()),
1007        "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs),
1008        "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty),
1009        "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"),
1010        "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"),
1011        "LIKE": lambda self: self._parse_create_like(),
1012        "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty),
1013        "LOCK": lambda self: self._parse_locking(),
1014        "LOCKING": lambda self: self._parse_locking(),
1015        "LOG": lambda self, **kwargs: self._parse_log(**kwargs),
1016        "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty),
1017        "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs),
1018        "MODIFIES": lambda self: self._parse_modifies_property(),
1019        "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True),
1020        "NO": lambda self: self._parse_no_property(),
1021        "ON": lambda self: self._parse_on_property(),
1022        "ORDER BY": lambda self: self._parse_order(skip_order_token=True),
1023        "OUTPUT": lambda self: self.expression(exp.OutputModelProperty, this=self._parse_schema()),
1024        "PARTITION": lambda self: self._parse_partitioned_of(),
1025        "PARTITION BY": lambda self: self._parse_partitioned_by(),
1026        "PARTITIONED BY": lambda self: self._parse_partitioned_by(),
1027        "PARTITIONED_BY": lambda self: self._parse_partitioned_by(),
1028        "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True),
1029        "RANGE": lambda self: self._parse_dict_range(this="RANGE"),
1030        "READS": lambda self: self._parse_reads_property(),
1031        "REMOTE": lambda self: self._parse_remote_with_connection(),
1032        "RETURNS": lambda self: self._parse_returns(),
1033        "STRICT": lambda self: self.expression(exp.StrictProperty),
1034        "STREAMING": lambda self: self.expression(exp.StreamingTableProperty),
1035        "ROW": lambda self: self._parse_row(),
1036        "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty),
1037        "SAMPLE": lambda self: self.expression(
1038            exp.SampleProperty, this=self._match_text_seq("BY") and self._parse_bitwise()
1039        ),
1040        "SECURE": lambda self: self.expression(exp.SecureProperty),
1041        "SECURITY": lambda self: self._parse_security(),
1042        "SET": lambda self: self.expression(exp.SetProperty, multi=False),
1043        "SETTINGS": lambda self: self._parse_settings_property(),
1044        "SHARING": lambda self: self._parse_property_assignment(exp.SharingProperty),
1045        "SORTKEY": lambda self: self._parse_sortkey(),
1046        "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"),
1047        "STABLE": lambda self: self.expression(
1048            exp.StabilityProperty, this=exp.Literal.string("STABLE")
1049        ),
1050        "STORED": lambda self: self._parse_stored(),
1051        "SYSTEM_VERSIONING": lambda self: self._parse_system_versioning_property(),
1052        "TBLPROPERTIES": lambda self: self._parse_wrapped_properties(),
1053        "TEMP": lambda self: self.expression(exp.TemporaryProperty),
1054        "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty),
1055        "TO": lambda self: self._parse_to_table(),
1056        "TRANSIENT": lambda self: self.expression(exp.TransientProperty),
1057        "TRANSFORM": lambda self: self.expression(
1058            exp.TransformModelProperty, expressions=self._parse_wrapped_csv(self._parse_expression)
1059        ),
1060        "TTL": lambda self: self._parse_ttl(),
1061        "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
1062        "UNLOGGED": lambda self: self.expression(exp.UnloggedProperty),
1063        "VOLATILE": lambda self: self._parse_volatile_property(),
1064        "WITH": lambda self: self._parse_with_property(),
1065    }
1066
1067    CONSTRAINT_PARSERS = {
1068        "AUTOINCREMENT": lambda self: self._parse_auto_increment(),
1069        "AUTO_INCREMENT": lambda self: self._parse_auto_increment(),
1070        "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False),
1071        "CHARACTER SET": lambda self: self.expression(
1072            exp.CharacterSetColumnConstraint, this=self._parse_var_or_string()
1073        ),
1074        "CHECK": lambda self: self.expression(
1075            exp.CheckColumnConstraint,
1076            this=self._parse_wrapped(self._parse_assignment),
1077            enforced=self._match_text_seq("ENFORCED"),
1078        ),
1079        "COLLATE": lambda self: self.expression(
1080            exp.CollateColumnConstraint,
1081            this=self._parse_identifier() or self._parse_column(),
1082        ),
1083        "COMMENT": lambda self: self.expression(
1084            exp.CommentColumnConstraint, this=self._parse_string()
1085        ),
1086        "COMPRESS": lambda self: self._parse_compress(),
1087        "CLUSTERED": lambda self: self.expression(
1088            exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1089        ),
1090        "NONCLUSTERED": lambda self: self.expression(
1091            exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1092        ),
1093        "DEFAULT": lambda self: self.expression(
1094            exp.DefaultColumnConstraint, this=self._parse_bitwise()
1095        ),
1096        "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()),
1097        "EPHEMERAL": lambda self: self.expression(
1098            exp.EphemeralColumnConstraint, this=self._parse_bitwise()
1099        ),
1100        "EXCLUDE": lambda self: self.expression(
1101            exp.ExcludeColumnConstraint, this=self._parse_index_params()
1102        ),
1103        "FOREIGN KEY": lambda self: self._parse_foreign_key(),
1104        "FORMAT": lambda self: self.expression(
1105            exp.DateFormatColumnConstraint, this=self._parse_var_or_string()
1106        ),
1107        "GENERATED": lambda self: self._parse_generated_as_identity(),
1108        "IDENTITY": lambda self: self._parse_auto_increment(),
1109        "INLINE": lambda self: self._parse_inline(),
1110        "LIKE": lambda self: self._parse_create_like(),
1111        "NOT": lambda self: self._parse_not_constraint(),
1112        "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True),
1113        "ON": lambda self: (
1114            self._match(TokenType.UPDATE)
1115            and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function())
1116        )
1117        or self.expression(exp.OnProperty, this=self._parse_id_var()),
1118        "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()),
1119        "PERIOD": lambda self: self._parse_period_for_system_time(),
1120        "PRIMARY KEY": lambda self: self._parse_primary_key(),
1121        "REFERENCES": lambda self: self._parse_references(match=False),
1122        "TITLE": lambda self: self.expression(
1123            exp.TitleColumnConstraint, this=self._parse_var_or_string()
1124        ),
1125        "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]),
1126        "UNIQUE": lambda self: self._parse_unique(),
1127        "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint),
1128        "WATERMARK": lambda self: self.expression(
1129            exp.WatermarkColumnConstraint,
1130            this=self._match(TokenType.FOR) and self._parse_column(),
1131            expression=self._match(TokenType.ALIAS) and self._parse_disjunction(),
1132        ),
1133        "WITH": lambda self: self.expression(
1134            exp.Properties, expressions=self._parse_wrapped_properties()
1135        ),
1136        "BUCKET": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1137        "TRUNCATE": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1138    }
1139
1140    def _parse_partitioned_by_bucket_or_truncate(self) -> exp.Expression:
1141        klass = (
1142            exp.PartitionedByBucket
1143            if self._prev.text.upper() == "BUCKET"
1144            else exp.PartitionByTruncate
1145        )
1146
1147        args = self._parse_wrapped_csv(lambda: self._parse_primary() or self._parse_column())
1148        this, expression = seq_get(args, 0), seq_get(args, 1)
1149
1150        if isinstance(this, exp.Literal):
1151            # Check for Iceberg partition transforms (bucket / truncate) and ensure their arguments are in the right order
1152            #  - For Hive, it's `bucket(<num buckets>, <col name>)` or `truncate(<num_chars>, <col_name>)`
1153            #  - For Trino, it's reversed - `bucket(<col name>, <num buckets>)` or `truncate(<col_name>, <num_chars>)`
1154            # Both variants are canonicalized in the latter i.e `bucket(<col name>, <num buckets>)`
1155            #
1156            # Hive ref: https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-partitioning
1157            # Trino ref: https://docs.aws.amazon.com/athena/latest/ug/create-table-as.html#ctas-table-properties
1158            this, expression = expression, this
1159
1160        return self.expression(klass, this=this, expression=expression)
1161
1162    ALTER_PARSERS = {
1163        "ADD": lambda self: self._parse_alter_table_add(),
1164        "AS": lambda self: self._parse_select(),
1165        "ALTER": lambda self: self._parse_alter_table_alter(),
1166        "CLUSTER BY": lambda self: self._parse_cluster(wrapped=True),
1167        "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()),
1168        "DROP": lambda self: self._parse_alter_table_drop(),
1169        "RENAME": lambda self: self._parse_alter_table_rename(),
1170        "SET": lambda self: self._parse_alter_table_set(),
1171        "SWAP": lambda self: self.expression(
1172            exp.SwapTable, this=self._match(TokenType.WITH) and self._parse_table(schema=True)
1173        ),
1174    }
1175
1176    ALTER_ALTER_PARSERS = {
1177        "DISTKEY": lambda self: self._parse_alter_diststyle(),
1178        "DISTSTYLE": lambda self: self._parse_alter_diststyle(),
1179        "SORTKEY": lambda self: self._parse_alter_sortkey(),
1180        "COMPOUND": lambda self: self._parse_alter_sortkey(compound=True),
1181    }
1182
1183    SCHEMA_UNNAMED_CONSTRAINTS = {
1184        "CHECK",
1185        "EXCLUDE",
1186        "FOREIGN KEY",
1187        "LIKE",
1188        "PERIOD",
1189        "PRIMARY KEY",
1190        "UNIQUE",
1191        "WATERMARK",
1192        "BUCKET",
1193        "TRUNCATE",
1194    }
1195
1196    NO_PAREN_FUNCTION_PARSERS = {
1197        "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()),
1198        "CASE": lambda self: self._parse_case(),
1199        "CONNECT_BY_ROOT": lambda self: self.expression(
1200            exp.ConnectByRoot, this=self._parse_column()
1201        ),
1202        "IF": lambda self: self._parse_if(),
1203    }
1204
1205    INVALID_FUNC_NAME_TOKENS = {
1206        TokenType.IDENTIFIER,
1207        TokenType.STRING,
1208    }
1209
1210    FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
1211
1212    KEY_VALUE_DEFINITIONS = (exp.Alias, exp.EQ, exp.PropertyEQ, exp.Slice)
1213
1214    FUNCTION_PARSERS = {
1215        **{
1216            name: lambda self: self._parse_max_min_by(exp.ArgMax) for name in exp.ArgMax.sql_names()
1217        },
1218        **{
1219            name: lambda self: self._parse_max_min_by(exp.ArgMin) for name in exp.ArgMin.sql_names()
1220        },
1221        "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
1222        "CEIL": lambda self: self._parse_ceil_floor(exp.Ceil),
1223        "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
1224        "DECODE": lambda self: self._parse_decode(),
1225        "EXTRACT": lambda self: self._parse_extract(),
1226        "FLOOR": lambda self: self._parse_ceil_floor(exp.Floor),
1227        "GAP_FILL": lambda self: self._parse_gap_fill(),
1228        "JSON_OBJECT": lambda self: self._parse_json_object(),
1229        "JSON_OBJECTAGG": lambda self: self._parse_json_object(agg=True),
1230        "JSON_TABLE": lambda self: self._parse_json_table(),
1231        "MATCH": lambda self: self._parse_match_against(),
1232        "NORMALIZE": lambda self: self._parse_normalize(),
1233        "OPENJSON": lambda self: self._parse_open_json(),
1234        "OVERLAY": lambda self: self._parse_overlay(),
1235        "POSITION": lambda self: self._parse_position(),
1236        "PREDICT": lambda self: self._parse_predict(),
1237        "SAFE_CAST": lambda self: self._parse_cast(False, safe=True),
1238        "STRING_AGG": lambda self: self._parse_string_agg(),
1239        "SUBSTRING": lambda self: self._parse_substring(),
1240        "TRIM": lambda self: self._parse_trim(),
1241        "TRY_CAST": lambda self: self._parse_cast(False, safe=True),
1242        "TRY_CONVERT": lambda self: self._parse_convert(False, safe=True),
1243        "XMLELEMENT": lambda self: self.expression(
1244            exp.XMLElement,
1245            this=self._match_text_seq("NAME") and self._parse_id_var(),
1246            expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
1247        ),
1248        "XMLTABLE": lambda self: self._parse_xml_table(),
1249    }
1250
1251    QUERY_MODIFIER_PARSERS = {
1252        TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()),
1253        TokenType.PREWHERE: lambda self: ("prewhere", self._parse_prewhere()),
1254        TokenType.WHERE: lambda self: ("where", self._parse_where()),
1255        TokenType.GROUP_BY: lambda self: ("group", self._parse_group()),
1256        TokenType.HAVING: lambda self: ("having", self._parse_having()),
1257        TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()),
1258        TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()),
1259        TokenType.ORDER_BY: lambda self: ("order", self._parse_order()),
1260        TokenType.LIMIT: lambda self: ("limit", self._parse_limit()),
1261        TokenType.FETCH: lambda self: ("limit", self._parse_limit()),
1262        TokenType.OFFSET: lambda self: ("offset", self._parse_offset()),
1263        TokenType.FOR: lambda self: ("locks", self._parse_locks()),
1264        TokenType.LOCK: lambda self: ("locks", self._parse_locks()),
1265        TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1266        TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1267        TokenType.CLUSTER_BY: lambda self: (
1268            "cluster",
1269            self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
1270        ),
1271        TokenType.DISTRIBUTE_BY: lambda self: (
1272            "distribute",
1273            self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY),
1274        ),
1275        TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)),
1276        TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)),
1277        TokenType.START_WITH: lambda self: ("connect", self._parse_connect()),
1278    }
1279
1280    SET_PARSERS = {
1281        "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"),
1282        "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"),
1283        "SESSION": lambda self: self._parse_set_item_assignment("SESSION"),
1284        "TRANSACTION": lambda self: self._parse_set_transaction(),
1285    }
1286
1287    SHOW_PARSERS: t.Dict[str, t.Callable] = {}
1288
1289    TYPE_LITERAL_PARSERS = {
1290        exp.DataType.Type.JSON: lambda self, this, _: self.expression(exp.ParseJSON, this=this),
1291    }
1292
1293    TYPE_CONVERTERS: t.Dict[exp.DataType.Type, t.Callable[[exp.DataType], exp.DataType]] = {}
1294
1295    DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN}
1296
1297    PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE}
1298
1299    TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"}
1300    TRANSACTION_CHARACTERISTICS: OPTIONS_TYPE = {
1301        "ISOLATION": (
1302            ("LEVEL", "REPEATABLE", "READ"),
1303            ("LEVEL", "READ", "COMMITTED"),
1304            ("LEVEL", "READ", "UNCOMITTED"),
1305            ("LEVEL", "SERIALIZABLE"),
1306        ),
1307        "READ": ("WRITE", "ONLY"),
1308    }
1309
1310    CONFLICT_ACTIONS: OPTIONS_TYPE = dict.fromkeys(
1311        ("ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK", "UPDATE"), tuple()
1312    )
1313    CONFLICT_ACTIONS["DO"] = ("NOTHING", "UPDATE")
1314
1315    CREATE_SEQUENCE: OPTIONS_TYPE = {
1316        "SCALE": ("EXTEND", "NOEXTEND"),
1317        "SHARD": ("EXTEND", "NOEXTEND"),
1318        "NO": ("CYCLE", "CACHE", "MAXVALUE", "MINVALUE"),
1319        **dict.fromkeys(
1320            (
1321                "SESSION",
1322                "GLOBAL",
1323                "KEEP",
1324                "NOKEEP",
1325                "ORDER",
1326                "NOORDER",
1327                "NOCACHE",
1328                "CYCLE",
1329                "NOCYCLE",
1330                "NOMINVALUE",
1331                "NOMAXVALUE",
1332                "NOSCALE",
1333                "NOSHARD",
1334            ),
1335            tuple(),
1336        ),
1337    }
1338
1339    ISOLATED_LOADING_OPTIONS: OPTIONS_TYPE = {"FOR": ("ALL", "INSERT", "NONE")}
1340
1341    USABLES: OPTIONS_TYPE = dict.fromkeys(
1342        ("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA", "CATALOG"), tuple()
1343    )
1344
1345    CAST_ACTIONS: OPTIONS_TYPE = dict.fromkeys(("RENAME", "ADD"), ("FIELDS",))
1346
1347    SCHEMA_BINDING_OPTIONS: OPTIONS_TYPE = {
1348        "TYPE": ("EVOLUTION",),
1349        **dict.fromkeys(("BINDING", "COMPENSATION", "EVOLUTION"), tuple()),
1350    }
1351
1352    PROCEDURE_OPTIONS: OPTIONS_TYPE = {}
1353
1354    EXECUTE_AS_OPTIONS: OPTIONS_TYPE = dict.fromkeys(("CALLER", "SELF", "OWNER"), tuple())
1355
1356    KEY_CONSTRAINT_OPTIONS: OPTIONS_TYPE = {
1357        "NOT": ("ENFORCED",),
1358        "MATCH": (
1359            "FULL",
1360            "PARTIAL",
1361            "SIMPLE",
1362        ),
1363        "INITIALLY": ("DEFERRED", "IMMEDIATE"),
1364        "USING": (
1365            "BTREE",
1366            "HASH",
1367        ),
1368        **dict.fromkeys(("DEFERRABLE", "NORELY", "RELY"), tuple()),
1369    }
1370
1371    WINDOW_EXCLUDE_OPTIONS: OPTIONS_TYPE = {
1372        "NO": ("OTHERS",),
1373        "CURRENT": ("ROW",),
1374        **dict.fromkeys(("GROUP", "TIES"), tuple()),
1375    }
1376
1377    INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"}
1378
1379    CLONE_KEYWORDS = {"CLONE", "COPY"}
1380    HISTORICAL_DATA_PREFIX = {"AT", "BEFORE", "END"}
1381    HISTORICAL_DATA_KIND = {"OFFSET", "STATEMENT", "STREAM", "TIMESTAMP", "VERSION"}
1382
1383    OPCLASS_FOLLOW_KEYWORDS = {"ASC", "DESC", "NULLS", "WITH"}
1384
1385    OPTYPE_FOLLOW_TOKENS = {TokenType.COMMA, TokenType.R_PAREN}
1386
1387    TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE}
1388
1389    VIEW_ATTRIBUTES = {"ENCRYPTION", "SCHEMABINDING", "VIEW_METADATA"}
1390
1391    WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS}
1392    WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER}
1393    WINDOW_SIDES = {"FOLLOWING", "PRECEDING"}
1394
1395    JSON_KEY_VALUE_SEPARATOR_TOKENS = {TokenType.COLON, TokenType.COMMA, TokenType.IS}
1396
1397    FETCH_TOKENS = ID_VAR_TOKENS - {TokenType.ROW, TokenType.ROWS, TokenType.PERCENT}
1398
1399    ADD_CONSTRAINT_TOKENS = {
1400        TokenType.CONSTRAINT,
1401        TokenType.FOREIGN_KEY,
1402        TokenType.INDEX,
1403        TokenType.KEY,
1404        TokenType.PRIMARY_KEY,
1405        TokenType.UNIQUE,
1406    }
1407
1408    DISTINCT_TOKENS = {TokenType.DISTINCT}
1409
1410    NULL_TOKENS = {TokenType.NULL}
1411
1412    UNNEST_OFFSET_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - SET_OPERATIONS
1413
1414    SELECT_START_TOKENS = {TokenType.L_PAREN, TokenType.WITH, TokenType.SELECT}
1415
1416    COPY_INTO_VARLEN_OPTIONS = {"FILE_FORMAT", "COPY_OPTIONS", "FORMAT_OPTIONS", "CREDENTIAL"}
1417
1418    IS_JSON_PREDICATE_KIND = {"VALUE", "SCALAR", "ARRAY", "OBJECT"}
1419
1420    ODBC_DATETIME_LITERALS = {
1421        "d": exp.Date,
1422        "t": exp.Time,
1423        "ts": exp.Timestamp,
1424    }
1425
1426    ON_CONDITION_TOKENS = {"ERROR", "NULL", "TRUE", "FALSE", "EMPTY"}
1427
1428    PRIVILEGE_FOLLOW_TOKENS = {TokenType.ON, TokenType.COMMA, TokenType.L_PAREN}
1429
1430    # The style options for the DESCRIBE statement
1431    DESCRIBE_STYLES = {"ANALYZE", "EXTENDED", "FORMATTED", "HISTORY"}
1432
1433    # The style options for the ANALYZE statement
1434    ANALYZE_STYLES = {
1435        "BUFFER_USAGE_LIMIT",
1436        "FULL",
1437        "LOCAL",
1438        "NO_WRITE_TO_BINLOG",
1439        "SAMPLE",
1440        "SKIP_LOCKED",
1441        "VERBOSE",
1442    }
1443
1444    ANALYZE_EXPRESSION_PARSERS = {
1445        "ALL": lambda self: self._parse_analyze_columns(),
1446        "COMPUTE": lambda self: self._parse_analyze_statistics(),
1447        "DELETE": lambda self: self._parse_analyze_delete(),
1448        "DROP": lambda self: self._parse_analyze_histogram(),
1449        "ESTIMATE": lambda self: self._parse_analyze_statistics(),
1450        "LIST": lambda self: self._parse_analyze_list(),
1451        "PREDICATE": lambda self: self._parse_analyze_columns(),
1452        "UPDATE": lambda self: self._parse_analyze_histogram(),
1453        "VALIDATE": lambda self: self._parse_analyze_validate(),
1454    }
1455
1456    PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
1457
1458    AMBIGUOUS_ALIAS_TOKENS = (TokenType.LIMIT, TokenType.OFFSET)
1459
1460    OPERATION_MODIFIERS: t.Set[str] = set()
1461
1462    RECURSIVE_CTE_SEARCH_KIND = {"BREADTH", "DEPTH", "CYCLE"}
1463
1464    MODIFIABLES = (exp.Query, exp.Table, exp.TableFromRows)
1465
1466    STRICT_CAST = True
1467
1468    PREFIXED_PIVOT_COLUMNS = False
1469    IDENTIFY_PIVOT_STRINGS = False
1470
1471    LOG_DEFAULTS_TO_LN = False
1472
1473    # Whether the table sample clause expects CSV syntax
1474    TABLESAMPLE_CSV = False
1475
1476    # The default method used for table sampling
1477    DEFAULT_SAMPLING_METHOD: t.Optional[str] = None
1478
1479    # Whether the SET command needs a delimiter (e.g. "=") for assignments
1480    SET_REQUIRES_ASSIGNMENT_DELIMITER = True
1481
1482    # Whether the TRIM function expects the characters to trim as its first argument
1483    TRIM_PATTERN_FIRST = False
1484
1485    # Whether string aliases are supported `SELECT COUNT(*) 'count'`
1486    STRING_ALIASES = False
1487
1488    # Whether query modifiers such as LIMIT are attached to the UNION node (vs its right operand)
1489    MODIFIERS_ATTACHED_TO_SET_OP = True
1490    SET_OP_MODIFIERS = {"order", "limit", "offset"}
1491
1492    # Whether to parse IF statements that aren't followed by a left parenthesis as commands
1493    NO_PAREN_IF_COMMANDS = True
1494
1495    # Whether the -> and ->> operators expect documents of type JSON (e.g. Postgres)
1496    JSON_ARROWS_REQUIRE_JSON_TYPE = False
1497
1498    # Whether the `:` operator is used to extract a value from a VARIANT column
1499    COLON_IS_VARIANT_EXTRACT = False
1500
1501    # Whether or not a VALUES keyword needs to be followed by '(' to form a VALUES clause.
1502    # If this is True and '(' is not found, the keyword will be treated as an identifier
1503    VALUES_FOLLOWED_BY_PAREN = True
1504
1505    # Whether implicit unnesting is supported, e.g. SELECT 1 FROM y.z AS z, z.a (Redshift)
1506    SUPPORTS_IMPLICIT_UNNEST = False
1507
1508    # Whether or not interval spans are supported, INTERVAL 1 YEAR TO MONTHS
1509    INTERVAL_SPANS = True
1510
1511    # Whether a PARTITION clause can follow a table reference
1512    SUPPORTS_PARTITION_SELECTION = False
1513
1514    # Whether the `name AS expr` schema/column constraint requires parentheses around `expr`
1515    WRAPPED_TRANSFORM_COLUMN_CONSTRAINT = True
1516
1517    # Whether the 'AS' keyword is optional in the CTE definition syntax
1518    OPTIONAL_ALIAS_TOKEN_CTE = True
1519
1520    # Whether renaming a column with an ALTER statement requires the presence of the COLUMN keyword
1521    ALTER_RENAME_REQUIRES_COLUMN = True
1522
1523    # Whether all join types have the same precedence, i.e., they "naturally" produce a left-deep tree.
1524    # In standard SQL, joins that use the JOIN keyword take higher precedence than comma-joins. That is
1525    # to say, JOIN operators happen before comma operators. This is not the case in some dialects, such
1526    # as BigQuery, where all joins have the same precedence.
1527    JOINS_HAVE_EQUAL_PRECEDENCE = False
1528
1529    # Whether TIMESTAMP <literal> can produce a zone-aware timestamp
1530    ZONE_AWARE_TIMESTAMP_CONSTRUCTOR = False
1531
1532    __slots__ = (
1533        "error_level",
1534        "error_message_context",
1535        "max_errors",
1536        "dialect",
1537        "sql",
1538        "errors",
1539        "_tokens",
1540        "_index",
1541        "_curr",
1542        "_next",
1543        "_prev",
1544        "_prev_comments",
1545        "_pipe_cte_counter",
1546    )
1547
1548    # Autofilled
1549    SHOW_TRIE: t.Dict = {}
1550    SET_TRIE: t.Dict = {}
1551
1552    def __init__(
1553        self,
1554        error_level: t.Optional[ErrorLevel] = None,
1555        error_message_context: int = 100,
1556        max_errors: int = 3,
1557        dialect: DialectType = None,
1558    ):
1559        from sqlglot.dialects import Dialect
1560
1561        self.error_level = error_level or ErrorLevel.IMMEDIATE
1562        self.error_message_context = error_message_context
1563        self.max_errors = max_errors
1564        self.dialect = Dialect.get_or_raise(dialect)
1565        self.reset()
1566
1567    def reset(self):
1568        self.sql = ""
1569        self.errors = []
1570        self._tokens = []
1571        self._index = 0
1572        self._curr = None
1573        self._next = None
1574        self._prev = None
1575        self._prev_comments = None
1576        self._pipe_cte_counter = 0
1577
1578    def parse(
1579        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1580    ) -> t.List[t.Optional[exp.Expression]]:
1581        """
1582        Parses a list of tokens and returns a list of syntax trees, one tree
1583        per parsed SQL statement.
1584
1585        Args:
1586            raw_tokens: The list of tokens.
1587            sql: The original SQL string, used to produce helpful debug messages.
1588
1589        Returns:
1590            The list of the produced syntax trees.
1591        """
1592        return self._parse(
1593            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1594        )
1595
1596    def parse_into(
1597        self,
1598        expression_types: exp.IntoType,
1599        raw_tokens: t.List[Token],
1600        sql: t.Optional[str] = None,
1601    ) -> t.List[t.Optional[exp.Expression]]:
1602        """
1603        Parses a list of tokens into a given Expression type. If a collection of Expression
1604        types is given instead, this method will try to parse the token list into each one
1605        of them, stopping at the first for which the parsing succeeds.
1606
1607        Args:
1608            expression_types: The expression type(s) to try and parse the token list into.
1609            raw_tokens: The list of tokens.
1610            sql: The original SQL string, used to produce helpful debug messages.
1611
1612        Returns:
1613            The target Expression.
1614        """
1615        errors = []
1616        for expression_type in ensure_list(expression_types):
1617            parser = self.EXPRESSION_PARSERS.get(expression_type)
1618            if not parser:
1619                raise TypeError(f"No parser registered for {expression_type}")
1620
1621            try:
1622                return self._parse(parser, raw_tokens, sql)
1623            except ParseError as e:
1624                e.errors[0]["into_expression"] = expression_type
1625                errors.append(e)
1626
1627        raise ParseError(
1628            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1629            errors=merge_errors(errors),
1630        ) from errors[-1]
1631
1632    def _parse(
1633        self,
1634        parse_method: t.Callable[[Parser], t.Optional[exp.Expression]],
1635        raw_tokens: t.List[Token],
1636        sql: t.Optional[str] = None,
1637    ) -> t.List[t.Optional[exp.Expression]]:
1638        self.reset()
1639        self.sql = sql or ""
1640
1641        total = len(raw_tokens)
1642        chunks: t.List[t.List[Token]] = [[]]
1643
1644        for i, token in enumerate(raw_tokens):
1645            if token.token_type == TokenType.SEMICOLON:
1646                if token.comments:
1647                    chunks.append([token])
1648
1649                if i < total - 1:
1650                    chunks.append([])
1651            else:
1652                chunks[-1].append(token)
1653
1654        expressions = []
1655
1656        for tokens in chunks:
1657            self._index = -1
1658            self._tokens = tokens
1659            self._advance()
1660
1661            expressions.append(parse_method(self))
1662
1663            if self._index < len(self._tokens):
1664                self.raise_error("Invalid expression / Unexpected token")
1665
1666            self.check_errors()
1667
1668        return expressions
1669
1670    def check_errors(self) -> None:
1671        """Logs or raises any found errors, depending on the chosen error level setting."""
1672        if self.error_level == ErrorLevel.WARN:
1673            for error in self.errors:
1674                logger.error(str(error))
1675        elif self.error_level == ErrorLevel.RAISE and self.errors:
1676            raise ParseError(
1677                concat_messages(self.errors, self.max_errors),
1678                errors=merge_errors(self.errors),
1679            )
1680
1681    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1682        """
1683        Appends an error in the list of recorded errors or raises it, depending on the chosen
1684        error level setting.
1685        """
1686        token = token or self._curr or self._prev or Token.string("")
1687        start = token.start
1688        end = token.end + 1
1689        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1690        highlight = self.sql[start:end]
1691        end_context = self.sql[end : end + self.error_message_context]
1692
1693        error = ParseError.new(
1694            f"{message}. Line {token.line}, Col: {token.col}.\n"
1695            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1696            description=message,
1697            line=token.line,
1698            col=token.col,
1699            start_context=start_context,
1700            highlight=highlight,
1701            end_context=end_context,
1702        )
1703
1704        if self.error_level == ErrorLevel.IMMEDIATE:
1705            raise error
1706
1707        self.errors.append(error)
1708
1709    def expression(
1710        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1711    ) -> E:
1712        """
1713        Creates a new, validated Expression.
1714
1715        Args:
1716            exp_class: The expression class to instantiate.
1717            comments: An optional list of comments to attach to the expression.
1718            kwargs: The arguments to set for the expression along with their respective values.
1719
1720        Returns:
1721            The target expression.
1722        """
1723        instance = exp_class(**kwargs)
1724        instance.add_comments(comments) if comments else self._add_comments(instance)
1725        return self.validate_expression(instance)
1726
1727    def _add_comments(self, expression: t.Optional[exp.Expression]) -> None:
1728        if expression and self._prev_comments:
1729            expression.add_comments(self._prev_comments)
1730            self._prev_comments = None
1731
1732    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1733        """
1734        Validates an Expression, making sure that all its mandatory arguments are set.
1735
1736        Args:
1737            expression: The expression to validate.
1738            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1739
1740        Returns:
1741            The validated expression.
1742        """
1743        if self.error_level != ErrorLevel.IGNORE:
1744            for error_message in expression.error_messages(args):
1745                self.raise_error(error_message)
1746
1747        return expression
1748
1749    def _find_sql(self, start: Token, end: Token) -> str:
1750        return self.sql[start.start : end.end + 1]
1751
1752    def _is_connected(self) -> bool:
1753        return self._prev and self._curr and self._prev.end + 1 == self._curr.start
1754
1755    def _advance(self, times: int = 1) -> None:
1756        self._index += times
1757        self._curr = seq_get(self._tokens, self._index)
1758        self._next = seq_get(self._tokens, self._index + 1)
1759
1760        if self._index > 0:
1761            self._prev = self._tokens[self._index - 1]
1762            self._prev_comments = self._prev.comments
1763        else:
1764            self._prev = None
1765            self._prev_comments = None
1766
1767    def _retreat(self, index: int) -> None:
1768        if index != self._index:
1769            self._advance(index - self._index)
1770
1771    def _warn_unsupported(self) -> None:
1772        if len(self._tokens) <= 1:
1773            return
1774
1775        # We use _find_sql because self.sql may comprise multiple chunks, and we're only
1776        # interested in emitting a warning for the one being currently processed.
1777        sql = self._find_sql(self._tokens[0], self._tokens[-1])[: self.error_message_context]
1778
1779        logger.warning(
1780            f"'{sql}' contains unsupported syntax. Falling back to parsing as a 'Command'."
1781        )
1782
1783    def _parse_command(self) -> exp.Command:
1784        self._warn_unsupported()
1785        return self.expression(
1786            exp.Command,
1787            comments=self._prev_comments,
1788            this=self._prev.text.upper(),
1789            expression=self._parse_string(),
1790        )
1791
1792    def _try_parse(self, parse_method: t.Callable[[], T], retreat: bool = False) -> t.Optional[T]:
1793        """
1794        Attemps to backtrack if a parse function that contains a try/catch internally raises an error.
1795        This behavior can be different depending on the uset-set ErrorLevel, so _try_parse aims to
1796        solve this by setting & resetting the parser state accordingly
1797        """
1798        index = self._index
1799        error_level = self.error_level
1800
1801        self.error_level = ErrorLevel.IMMEDIATE
1802        try:
1803            this = parse_method()
1804        except ParseError:
1805            this = None
1806        finally:
1807            if not this or retreat:
1808                self._retreat(index)
1809            self.error_level = error_level
1810
1811        return this
1812
1813    def _parse_comment(self, allow_exists: bool = True) -> exp.Expression:
1814        start = self._prev
1815        exists = self._parse_exists() if allow_exists else None
1816
1817        self._match(TokenType.ON)
1818
1819        materialized = self._match_text_seq("MATERIALIZED")
1820        kind = self._match_set(self.CREATABLES) and self._prev
1821        if not kind:
1822            return self._parse_as_command(start)
1823
1824        if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1825            this = self._parse_user_defined_function(kind=kind.token_type)
1826        elif kind.token_type == TokenType.TABLE:
1827            this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS)
1828        elif kind.token_type == TokenType.COLUMN:
1829            this = self._parse_column()
1830        else:
1831            this = self._parse_id_var()
1832
1833        self._match(TokenType.IS)
1834
1835        return self.expression(
1836            exp.Comment,
1837            this=this,
1838            kind=kind.text,
1839            expression=self._parse_string(),
1840            exists=exists,
1841            materialized=materialized,
1842        )
1843
1844    def _parse_to_table(
1845        self,
1846    ) -> exp.ToTableProperty:
1847        table = self._parse_table_parts(schema=True)
1848        return self.expression(exp.ToTableProperty, this=table)
1849
1850    # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl
1851    def _parse_ttl(self) -> exp.Expression:
1852        def _parse_ttl_action() -> t.Optional[exp.Expression]:
1853            this = self._parse_bitwise()
1854
1855            if self._match_text_seq("DELETE"):
1856                return self.expression(exp.MergeTreeTTLAction, this=this, delete=True)
1857            if self._match_text_seq("RECOMPRESS"):
1858                return self.expression(
1859                    exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise()
1860                )
1861            if self._match_text_seq("TO", "DISK"):
1862                return self.expression(
1863                    exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string()
1864                )
1865            if self._match_text_seq("TO", "VOLUME"):
1866                return self.expression(
1867                    exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string()
1868                )
1869
1870            return this
1871
1872        expressions = self._parse_csv(_parse_ttl_action)
1873        where = self._parse_where()
1874        group = self._parse_group()
1875
1876        aggregates = None
1877        if group and self._match(TokenType.SET):
1878            aggregates = self._parse_csv(self._parse_set_item)
1879
1880        return self.expression(
1881            exp.MergeTreeTTL,
1882            expressions=expressions,
1883            where=where,
1884            group=group,
1885            aggregates=aggregates,
1886        )
1887
1888    def _parse_statement(self) -> t.Optional[exp.Expression]:
1889        if self._curr is None:
1890            return None
1891
1892        if self._match_set(self.STATEMENT_PARSERS):
1893            comments = self._prev_comments
1894            stmt = self.STATEMENT_PARSERS[self._prev.token_type](self)
1895            stmt.add_comments(comments, prepend=True)
1896            return stmt
1897
1898        if self._match_set(self.dialect.tokenizer_class.COMMANDS):
1899            return self._parse_command()
1900
1901        expression = self._parse_expression()
1902        expression = self._parse_set_operations(expression) if expression else self._parse_select()
1903        return self._parse_query_modifiers(expression)
1904
1905    def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command:
1906        start = self._prev
1907        temporary = self._match(TokenType.TEMPORARY)
1908        materialized = self._match_text_seq("MATERIALIZED")
1909
1910        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
1911        if not kind:
1912            return self._parse_as_command(start)
1913
1914        concurrently = self._match_text_seq("CONCURRENTLY")
1915        if_exists = exists or self._parse_exists()
1916
1917        if kind == "COLUMN":
1918            this = self._parse_column()
1919        else:
1920            this = self._parse_table_parts(
1921                schema=True, is_db_reference=self._prev.token_type == TokenType.SCHEMA
1922            )
1923
1924        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
1925
1926        if self._match(TokenType.L_PAREN, advance=False):
1927            expressions = self._parse_wrapped_csv(self._parse_types)
1928        else:
1929            expressions = None
1930
1931        return self.expression(
1932            exp.Drop,
1933            exists=if_exists,
1934            this=this,
1935            expressions=expressions,
1936            kind=self.dialect.CREATABLE_KIND_MAPPING.get(kind) or kind,
1937            temporary=temporary,
1938            materialized=materialized,
1939            cascade=self._match_text_seq("CASCADE"),
1940            constraints=self._match_text_seq("CONSTRAINTS"),
1941            purge=self._match_text_seq("PURGE"),
1942            cluster=cluster,
1943            concurrently=concurrently,
1944        )
1945
1946    def _parse_exists(self, not_: bool = False) -> t.Optional[bool]:
1947        return (
1948            self._match_text_seq("IF")
1949            and (not not_ or self._match(TokenType.NOT))
1950            and self._match(TokenType.EXISTS)
1951        )
1952
1953    def _parse_create(self) -> exp.Create | exp.Command:
1954        # Note: this can't be None because we've matched a statement parser
1955        start = self._prev
1956
1957        replace = (
1958            start.token_type == TokenType.REPLACE
1959            or self._match_pair(TokenType.OR, TokenType.REPLACE)
1960            or self._match_pair(TokenType.OR, TokenType.ALTER)
1961        )
1962        refresh = self._match_pair(TokenType.OR, TokenType.REFRESH)
1963
1964        unique = self._match(TokenType.UNIQUE)
1965
1966        if self._match_text_seq("CLUSTERED", "COLUMNSTORE"):
1967            clustered = True
1968        elif self._match_text_seq("NONCLUSTERED", "COLUMNSTORE") or self._match_text_seq(
1969            "COLUMNSTORE"
1970        ):
1971            clustered = False
1972        else:
1973            clustered = None
1974
1975        if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False):
1976            self._advance()
1977
1978        properties = None
1979        create_token = self._match_set(self.CREATABLES) and self._prev
1980
1981        if not create_token:
1982            # exp.Properties.Location.POST_CREATE
1983            properties = self._parse_properties()
1984            create_token = self._match_set(self.CREATABLES) and self._prev
1985
1986            if not properties or not create_token:
1987                return self._parse_as_command(start)
1988
1989        concurrently = self._match_text_seq("CONCURRENTLY")
1990        exists = self._parse_exists(not_=True)
1991        this = None
1992        expression: t.Optional[exp.Expression] = None
1993        indexes = None
1994        no_schema_binding = None
1995        begin = None
1996        end = None
1997        clone = None
1998
1999        def extend_props(temp_props: t.Optional[exp.Properties]) -> None:
2000            nonlocal properties
2001            if properties and temp_props:
2002                properties.expressions.extend(temp_props.expressions)
2003            elif temp_props:
2004                properties = temp_props
2005
2006        if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
2007            this = self._parse_user_defined_function(kind=create_token.token_type)
2008
2009            # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature)
2010            extend_props(self._parse_properties())
2011
2012            expression = self._match(TokenType.ALIAS) and self._parse_heredoc()
2013            extend_props(self._parse_properties())
2014
2015            if not expression:
2016                if self._match(TokenType.COMMAND):
2017                    expression = self._parse_as_command(self._prev)
2018                else:
2019                    begin = self._match(TokenType.BEGIN)
2020                    return_ = self._match_text_seq("RETURN")
2021
2022                    if self._match(TokenType.STRING, advance=False):
2023                        # Takes care of BigQuery's JavaScript UDF definitions that end in an OPTIONS property
2024                        # # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement
2025                        expression = self._parse_string()
2026                        extend_props(self._parse_properties())
2027                    else:
2028                        expression = self._parse_user_defined_function_expression()
2029
2030                    end = self._match_text_seq("END")
2031
2032                    if return_:
2033                        expression = self.expression(exp.Return, this=expression)
2034        elif create_token.token_type == TokenType.INDEX:
2035            # Postgres allows anonymous indexes, eg. CREATE INDEX IF NOT EXISTS ON t(c)
2036            if not self._match(TokenType.ON):
2037                index = self._parse_id_var()
2038                anonymous = False
2039            else:
2040                index = None
2041                anonymous = True
2042
2043            this = self._parse_index(index=index, anonymous=anonymous)
2044        elif create_token.token_type in self.DB_CREATABLES:
2045            table_parts = self._parse_table_parts(
2046                schema=True, is_db_reference=create_token.token_type == TokenType.SCHEMA
2047            )
2048
2049            # exp.Properties.Location.POST_NAME
2050            self._match(TokenType.COMMA)
2051            extend_props(self._parse_properties(before=True))
2052
2053            this = self._parse_schema(this=table_parts)
2054
2055            # exp.Properties.Location.POST_SCHEMA and POST_WITH
2056            extend_props(self._parse_properties())
2057
2058            has_alias = self._match(TokenType.ALIAS)
2059            if not self._match_set(self.DDL_SELECT_TOKENS, advance=False):
2060                # exp.Properties.Location.POST_ALIAS
2061                extend_props(self._parse_properties())
2062
2063            if create_token.token_type == TokenType.SEQUENCE:
2064                expression = self._parse_types()
2065                extend_props(self._parse_properties())
2066            else:
2067                expression = self._parse_ddl_select()
2068
2069                # Some dialects also support using a table as an alias instead of a SELECT.
2070                # Here we fallback to this as an alternative.
2071                if not expression and has_alias:
2072                    expression = self._try_parse(self._parse_table_parts)
2073
2074            if create_token.token_type == TokenType.TABLE:
2075                # exp.Properties.Location.POST_EXPRESSION
2076                extend_props(self._parse_properties())
2077
2078                indexes = []
2079                while True:
2080                    index = self._parse_index()
2081
2082                    # exp.Properties.Location.POST_INDEX
2083                    extend_props(self._parse_properties())
2084                    if not index:
2085                        break
2086                    else:
2087                        self._match(TokenType.COMMA)
2088                        indexes.append(index)
2089            elif create_token.token_type == TokenType.VIEW:
2090                if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"):
2091                    no_schema_binding = True
2092            elif create_token.token_type in (TokenType.SINK, TokenType.SOURCE):
2093                extend_props(self._parse_properties())
2094
2095            shallow = self._match_text_seq("SHALLOW")
2096
2097            if self._match_texts(self.CLONE_KEYWORDS):
2098                copy = self._prev.text.lower() == "copy"
2099                clone = self.expression(
2100                    exp.Clone, this=self._parse_table(schema=True), shallow=shallow, copy=copy
2101                )
2102
2103        if self._curr and not self._match_set((TokenType.R_PAREN, TokenType.COMMA), advance=False):
2104            return self._parse_as_command(start)
2105
2106        create_kind_text = create_token.text.upper()
2107        return self.expression(
2108            exp.Create,
2109            this=this,
2110            kind=self.dialect.CREATABLE_KIND_MAPPING.get(create_kind_text) or create_kind_text,
2111            replace=replace,
2112            refresh=refresh,
2113            unique=unique,
2114            expression=expression,
2115            exists=exists,
2116            properties=properties,
2117            indexes=indexes,
2118            no_schema_binding=no_schema_binding,
2119            begin=begin,
2120            end=end,
2121            clone=clone,
2122            concurrently=concurrently,
2123            clustered=clustered,
2124        )
2125
2126    def _parse_sequence_properties(self) -> t.Optional[exp.SequenceProperties]:
2127        seq = exp.SequenceProperties()
2128
2129        options = []
2130        index = self._index
2131
2132        while self._curr:
2133            self._match(TokenType.COMMA)
2134            if self._match_text_seq("INCREMENT"):
2135                self._match_text_seq("BY")
2136                self._match_text_seq("=")
2137                seq.set("increment", self._parse_term())
2138            elif self._match_text_seq("MINVALUE"):
2139                seq.set("minvalue", self._parse_term())
2140            elif self._match_text_seq("MAXVALUE"):
2141                seq.set("maxvalue", self._parse_term())
2142            elif self._match(TokenType.START_WITH) or self._match_text_seq("START"):
2143                self._match_text_seq("=")
2144                seq.set("start", self._parse_term())
2145            elif self._match_text_seq("CACHE"):
2146                # T-SQL allows empty CACHE which is initialized dynamically
2147                seq.set("cache", self._parse_number() or True)
2148            elif self._match_text_seq("OWNED", "BY"):
2149                # "OWNED BY NONE" is the default
2150                seq.set("owned", None if self._match_text_seq("NONE") else self._parse_column())
2151            else:
2152                opt = self._parse_var_from_options(self.CREATE_SEQUENCE, raise_unmatched=False)
2153                if opt:
2154                    options.append(opt)
2155                else:
2156                    break
2157
2158        seq.set("options", options if options else None)
2159        return None if self._index == index else seq
2160
2161    def _parse_property_before(self) -> t.Optional[exp.Expression]:
2162        # only used for teradata currently
2163        self._match(TokenType.COMMA)
2164
2165        kwargs = {
2166            "no": self._match_text_seq("NO"),
2167            "dual": self._match_text_seq("DUAL"),
2168            "before": self._match_text_seq("BEFORE"),
2169            "default": self._match_text_seq("DEFAULT"),
2170            "local": (self._match_text_seq("LOCAL") and "LOCAL")
2171            or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"),
2172            "after": self._match_text_seq("AFTER"),
2173            "minimum": self._match_texts(("MIN", "MINIMUM")),
2174            "maximum": self._match_texts(("MAX", "MAXIMUM")),
2175        }
2176
2177        if self._match_texts(self.PROPERTY_PARSERS):
2178            parser = self.PROPERTY_PARSERS[self._prev.text.upper()]
2179            try:
2180                return parser(self, **{k: v for k, v in kwargs.items() if v})
2181            except TypeError:
2182                self.raise_error(f"Cannot parse property '{self._prev.text}'")
2183
2184        return None
2185
2186    def _parse_wrapped_properties(self) -> t.List[exp.Expression]:
2187        return self._parse_wrapped_csv(self._parse_property)
2188
2189    def _parse_property(self) -> t.Optional[exp.Expression]:
2190        if self._match_texts(self.PROPERTY_PARSERS):
2191            return self.PROPERTY_PARSERS[self._prev.text.upper()](self)
2192
2193        if self._match(TokenType.DEFAULT) and self._match_texts(self.PROPERTY_PARSERS):
2194            return self.PROPERTY_PARSERS[self._prev.text.upper()](self, default=True)
2195
2196        if self._match_text_seq("COMPOUND", "SORTKEY"):
2197            return self._parse_sortkey(compound=True)
2198
2199        if self._match_text_seq("SQL", "SECURITY"):
2200            return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
2201
2202        index = self._index
2203        key = self._parse_column()
2204
2205        if not self._match(TokenType.EQ):
2206            self._retreat(index)
2207            return self._parse_sequence_properties()
2208
2209        # Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
2210        if isinstance(key, exp.Column):
2211            key = key.to_dot() if len(key.parts) > 1 else exp.var(key.name)
2212
2213        value = self._parse_bitwise() or self._parse_var(any_token=True)
2214
2215        # Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
2216        if isinstance(value, exp.Column):
2217            value = exp.var(value.name)
2218
2219        return self.expression(exp.Property, this=key, value=value)
2220
2221    def _parse_stored(self) -> t.Union[exp.FileFormatProperty, exp.StorageHandlerProperty]:
2222        if self._match_text_seq("BY"):
2223            return self.expression(exp.StorageHandlerProperty, this=self._parse_var_or_string())
2224
2225        self._match(TokenType.ALIAS)
2226        input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None
2227        output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None
2228
2229        return self.expression(
2230            exp.FileFormatProperty,
2231            this=(
2232                self.expression(
2233                    exp.InputOutputFormat,
2234                    input_format=input_format,
2235                    output_format=output_format,
2236                )
2237                if input_format or output_format
2238                else self._parse_var_or_string() or self._parse_number() or self._parse_id_var()
2239            ),
2240        )
2241
2242    def _parse_unquoted_field(self) -> t.Optional[exp.Expression]:
2243        field = self._parse_field()
2244        if isinstance(field, exp.Identifier) and not field.quoted:
2245            field = exp.var(field)
2246
2247        return field
2248
2249    def _parse_property_assignment(self, exp_class: t.Type[E], **kwargs: t.Any) -> E:
2250        self._match(TokenType.EQ)
2251        self._match(TokenType.ALIAS)
2252
2253        return self.expression(exp_class, this=self._parse_unquoted_field(), **kwargs)
2254
2255    def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]:
2256        properties = []
2257        while True:
2258            if before:
2259                prop = self._parse_property_before()
2260            else:
2261                prop = self._parse_property()
2262            if not prop:
2263                break
2264            for p in ensure_list(prop):
2265                properties.append(p)
2266
2267        if properties:
2268            return self.expression(exp.Properties, expressions=properties)
2269
2270        return None
2271
2272    def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty:
2273        return self.expression(
2274            exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION")
2275        )
2276
2277    def _parse_security(self) -> t.Optional[exp.SecurityProperty]:
2278        if self._match_texts(("NONE", "DEFINER", "INVOKER")):
2279            security_specifier = self._prev.text.upper()
2280            return self.expression(exp.SecurityProperty, this=security_specifier)
2281        return None
2282
2283    def _parse_settings_property(self) -> exp.SettingsProperty:
2284        return self.expression(
2285            exp.SettingsProperty, expressions=self._parse_csv(self._parse_assignment)
2286        )
2287
2288    def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty:
2289        if self._index >= 2:
2290            pre_volatile_token = self._tokens[self._index - 2]
2291        else:
2292            pre_volatile_token = None
2293
2294        if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS:
2295            return exp.VolatileProperty()
2296
2297        return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE"))
2298
2299    def _parse_retention_period(self) -> exp.Var:
2300        # Parse TSQL's HISTORY_RETENTION_PERIOD: {INFINITE | <number> DAY | DAYS | MONTH ...}
2301        number = self._parse_number()
2302        number_str = f"{number} " if number else ""
2303        unit = self._parse_var(any_token=True)
2304        return exp.var(f"{number_str}{unit}")
2305
2306    def _parse_system_versioning_property(
2307        self, with_: bool = False
2308    ) -> exp.WithSystemVersioningProperty:
2309        self._match(TokenType.EQ)
2310        prop = self.expression(
2311            exp.WithSystemVersioningProperty,
2312            **{  # type: ignore
2313                "on": True,
2314                "with": with_,
2315            },
2316        )
2317
2318        if self._match_text_seq("OFF"):
2319            prop.set("on", False)
2320            return prop
2321
2322        self._match(TokenType.ON)
2323        if self._match(TokenType.L_PAREN):
2324            while self._curr and not self._match(TokenType.R_PAREN):
2325                if self._match_text_seq("HISTORY_TABLE", "="):
2326                    prop.set("this", self._parse_table_parts())
2327                elif self._match_text_seq("DATA_CONSISTENCY_CHECK", "="):
2328                    prop.set("data_consistency", self._advance_any() and self._prev.text.upper())
2329                elif self._match_text_seq("HISTORY_RETENTION_PERIOD", "="):
2330                    prop.set("retention_period", self._parse_retention_period())
2331
2332                self._match(TokenType.COMMA)
2333
2334        return prop
2335
2336    def _parse_data_deletion_property(self) -> exp.DataDeletionProperty:
2337        self._match(TokenType.EQ)
2338        on = self._match_text_seq("ON") or not self._match_text_seq("OFF")
2339        prop = self.expression(exp.DataDeletionProperty, on=on)
2340
2341        if self._match(TokenType.L_PAREN):
2342            while self._curr and not self._match(TokenType.R_PAREN):
2343                if self._match_text_seq("FILTER_COLUMN", "="):
2344                    prop.set("filter_column", self._parse_column())
2345                elif self._match_text_seq("RETENTION_PERIOD", "="):
2346                    prop.set("retention_period", self._parse_retention_period())
2347
2348                self._match(TokenType.COMMA)
2349
2350        return prop
2351
2352    def _parse_distributed_property(self) -> exp.DistributedByProperty:
2353        kind = "HASH"
2354        expressions: t.Optional[t.List[exp.Expression]] = None
2355        if self._match_text_seq("BY", "HASH"):
2356            expressions = self._parse_wrapped_csv(self._parse_id_var)
2357        elif self._match_text_seq("BY", "RANDOM"):
2358            kind = "RANDOM"
2359
2360        # If the BUCKETS keyword is not present, the number of buckets is AUTO
2361        buckets: t.Optional[exp.Expression] = None
2362        if self._match_text_seq("BUCKETS") and not self._match_text_seq("AUTO"):
2363            buckets = self._parse_number()
2364
2365        return self.expression(
2366            exp.DistributedByProperty,
2367            expressions=expressions,
2368            kind=kind,
2369            buckets=buckets,
2370            order=self._parse_order(),
2371        )
2372
2373    def _parse_composite_key_property(self, expr_type: t.Type[E]) -> E:
2374        self._match_text_seq("KEY")
2375        expressions = self._parse_wrapped_id_vars()
2376        return self.expression(expr_type, expressions=expressions)
2377
2378    def _parse_with_property(self) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
2379        if self._match_text_seq("(", "SYSTEM_VERSIONING"):
2380            prop = self._parse_system_versioning_property(with_=True)
2381            self._match_r_paren()
2382            return prop
2383
2384        if self._match(TokenType.L_PAREN, advance=False):
2385            return self._parse_wrapped_properties()
2386
2387        if self._match_text_seq("JOURNAL"):
2388            return self._parse_withjournaltable()
2389
2390        if self._match_texts(self.VIEW_ATTRIBUTES):
2391            return self.expression(exp.ViewAttributeProperty, this=self._prev.text.upper())
2392
2393        if self._match_text_seq("DATA"):
2394            return self._parse_withdata(no=False)
2395        elif self._match_text_seq("NO", "DATA"):
2396            return self._parse_withdata(no=True)
2397
2398        if self._match(TokenType.SERDE_PROPERTIES, advance=False):
2399            return self._parse_serde_properties(with_=True)
2400
2401        if self._match(TokenType.SCHEMA):
2402            return self.expression(
2403                exp.WithSchemaBindingProperty,
2404                this=self._parse_var_from_options(self.SCHEMA_BINDING_OPTIONS),
2405            )
2406
2407        if self._match_texts(self.PROCEDURE_OPTIONS, advance=False):
2408            return self.expression(
2409                exp.WithProcedureOptions, expressions=self._parse_csv(self._parse_procedure_option)
2410            )
2411
2412        if not self._next:
2413            return None
2414
2415        return self._parse_withisolatedloading()
2416
2417    def _parse_procedure_option(self) -> exp.Expression | None:
2418        if self._match_text_seq("EXECUTE", "AS"):
2419            return self.expression(
2420                exp.ExecuteAsProperty,
2421                this=self._parse_var_from_options(self.EXECUTE_AS_OPTIONS, raise_unmatched=False)
2422                or self._parse_string(),
2423            )
2424
2425        return self._parse_var_from_options(self.PROCEDURE_OPTIONS)
2426
2427    # https://dev.mysql.com/doc/refman/8.0/en/create-view.html
2428    def _parse_definer(self) -> t.Optional[exp.DefinerProperty]:
2429        self._match(TokenType.EQ)
2430
2431        user = self._parse_id_var()
2432        self._match(TokenType.PARAMETER)
2433        host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text)
2434
2435        if not user or not host:
2436            return None
2437
2438        return exp.DefinerProperty(this=f"{user}@{host}")
2439
2440    def _parse_withjournaltable(self) -> exp.WithJournalTableProperty:
2441        self._match(TokenType.TABLE)
2442        self._match(TokenType.EQ)
2443        return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts())
2444
2445    def _parse_log(self, no: bool = False) -> exp.LogProperty:
2446        return self.expression(exp.LogProperty, no=no)
2447
2448    def _parse_journal(self, **kwargs) -> exp.JournalProperty:
2449        return self.expression(exp.JournalProperty, **kwargs)
2450
2451    def _parse_checksum(self) -> exp.ChecksumProperty:
2452        self._match(TokenType.EQ)
2453
2454        on = None
2455        if self._match(TokenType.ON):
2456            on = True
2457        elif self._match_text_seq("OFF"):
2458            on = False
2459
2460        return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT))
2461
2462    def _parse_cluster(self, wrapped: bool = False) -> exp.Cluster:
2463        return self.expression(
2464            exp.Cluster,
2465            expressions=(
2466                self._parse_wrapped_csv(self._parse_ordered)
2467                if wrapped
2468                else self._parse_csv(self._parse_ordered)
2469            ),
2470        )
2471
2472    def _parse_clustered_by(self) -> exp.ClusteredByProperty:
2473        self._match_text_seq("BY")
2474
2475        self._match_l_paren()
2476        expressions = self._parse_csv(self._parse_column)
2477        self._match_r_paren()
2478
2479        if self._match_text_seq("SORTED", "BY"):
2480            self._match_l_paren()
2481            sorted_by = self._parse_csv(self._parse_ordered)
2482            self._match_r_paren()
2483        else:
2484            sorted_by = None
2485
2486        self._match(TokenType.INTO)
2487        buckets = self._parse_number()
2488        self._match_text_seq("BUCKETS")
2489
2490        return self.expression(
2491            exp.ClusteredByProperty,
2492            expressions=expressions,
2493            sorted_by=sorted_by,
2494            buckets=buckets,
2495        )
2496
2497    def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]:
2498        if not self._match_text_seq("GRANTS"):
2499            self._retreat(self._index - 1)
2500            return None
2501
2502        return self.expression(exp.CopyGrantsProperty)
2503
2504    def _parse_freespace(self) -> exp.FreespaceProperty:
2505        self._match(TokenType.EQ)
2506        return self.expression(
2507            exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT)
2508        )
2509
2510    def _parse_mergeblockratio(
2511        self, no: bool = False, default: bool = False
2512    ) -> exp.MergeBlockRatioProperty:
2513        if self._match(TokenType.EQ):
2514            return self.expression(
2515                exp.MergeBlockRatioProperty,
2516                this=self._parse_number(),
2517                percent=self._match(TokenType.PERCENT),
2518            )
2519
2520        return self.expression(exp.MergeBlockRatioProperty, no=no, default=default)
2521
2522    def _parse_datablocksize(
2523        self,
2524        default: t.Optional[bool] = None,
2525        minimum: t.Optional[bool] = None,
2526        maximum: t.Optional[bool] = None,
2527    ) -> exp.DataBlocksizeProperty:
2528        self._match(TokenType.EQ)
2529        size = self._parse_number()
2530
2531        units = None
2532        if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")):
2533            units = self._prev.text
2534
2535        return self.expression(
2536            exp.DataBlocksizeProperty,
2537            size=size,
2538            units=units,
2539            default=default,
2540            minimum=minimum,
2541            maximum=maximum,
2542        )
2543
2544    def _parse_blockcompression(self) -> exp.BlockCompressionProperty:
2545        self._match(TokenType.EQ)
2546        always = self._match_text_seq("ALWAYS")
2547        manual = self._match_text_seq("MANUAL")
2548        never = self._match_text_seq("NEVER")
2549        default = self._match_text_seq("DEFAULT")
2550
2551        autotemp = None
2552        if self._match_text_seq("AUTOTEMP"):
2553            autotemp = self._parse_schema()
2554
2555        return self.expression(
2556            exp.BlockCompressionProperty,
2557            always=always,
2558            manual=manual,
2559            never=never,
2560            default=default,
2561            autotemp=autotemp,
2562        )
2563
2564    def _parse_withisolatedloading(self) -> t.Optional[exp.IsolatedLoadingProperty]:
2565        index = self._index
2566        no = self._match_text_seq("NO")
2567        concurrent = self._match_text_seq("CONCURRENT")
2568
2569        if not self._match_text_seq("ISOLATED", "LOADING"):
2570            self._retreat(index)
2571            return None
2572
2573        target = self._parse_var_from_options(self.ISOLATED_LOADING_OPTIONS, raise_unmatched=False)
2574        return self.expression(
2575            exp.IsolatedLoadingProperty, no=no, concurrent=concurrent, target=target
2576        )
2577
2578    def _parse_locking(self) -> exp.LockingProperty:
2579        if self._match(TokenType.TABLE):
2580            kind = "TABLE"
2581        elif self._match(TokenType.VIEW):
2582            kind = "VIEW"
2583        elif self._match(TokenType.ROW):
2584            kind = "ROW"
2585        elif self._match_text_seq("DATABASE"):
2586            kind = "DATABASE"
2587        else:
2588            kind = None
2589
2590        if kind in ("DATABASE", "TABLE", "VIEW"):
2591            this = self._parse_table_parts()
2592        else:
2593            this = None
2594
2595        if self._match(TokenType.FOR):
2596            for_or_in = "FOR"
2597        elif self._match(TokenType.IN):
2598            for_or_in = "IN"
2599        else:
2600            for_or_in = None
2601
2602        if self._match_text_seq("ACCESS"):
2603            lock_type = "ACCESS"
2604        elif self._match_texts(("EXCL", "EXCLUSIVE")):
2605            lock_type = "EXCLUSIVE"
2606        elif self._match_text_seq("SHARE"):
2607            lock_type = "SHARE"
2608        elif self._match_text_seq("READ"):
2609            lock_type = "READ"
2610        elif self._match_text_seq("WRITE"):
2611            lock_type = "WRITE"
2612        elif self._match_text_seq("CHECKSUM"):
2613            lock_type = "CHECKSUM"
2614        else:
2615            lock_type = None
2616
2617        override = self._match_text_seq("OVERRIDE")
2618
2619        return self.expression(
2620            exp.LockingProperty,
2621            this=this,
2622            kind=kind,
2623            for_or_in=for_or_in,
2624            lock_type=lock_type,
2625            override=override,
2626        )
2627
2628    def _parse_partition_by(self) -> t.List[exp.Expression]:
2629        if self._match(TokenType.PARTITION_BY):
2630            return self._parse_csv(self._parse_assignment)
2631        return []
2632
2633    def _parse_partition_bound_spec(self) -> exp.PartitionBoundSpec:
2634        def _parse_partition_bound_expr() -> t.Optional[exp.Expression]:
2635            if self._match_text_seq("MINVALUE"):
2636                return exp.var("MINVALUE")
2637            if self._match_text_seq("MAXVALUE"):
2638                return exp.var("MAXVALUE")
2639            return self._parse_bitwise()
2640
2641        this: t.Optional[exp.Expression | t.List[exp.Expression]] = None
2642        expression = None
2643        from_expressions = None
2644        to_expressions = None
2645
2646        if self._match(TokenType.IN):
2647            this = self._parse_wrapped_csv(self._parse_bitwise)
2648        elif self._match(TokenType.FROM):
2649            from_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2650            self._match_text_seq("TO")
2651            to_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2652        elif self._match_text_seq("WITH", "(", "MODULUS"):
2653            this = self._parse_number()
2654            self._match_text_seq(",", "REMAINDER")
2655            expression = self._parse_number()
2656            self._match_r_paren()
2657        else:
2658            self.raise_error("Failed to parse partition bound spec.")
2659
2660        return self.expression(
2661            exp.PartitionBoundSpec,
2662            this=this,
2663            expression=expression,
2664            from_expressions=from_expressions,
2665            to_expressions=to_expressions,
2666        )
2667
2668    # https://www.postgresql.org/docs/current/sql-createtable.html
2669    def _parse_partitioned_of(self) -> t.Optional[exp.PartitionedOfProperty]:
2670        if not self._match_text_seq("OF"):
2671            self._retreat(self._index - 1)
2672            return None
2673
2674        this = self._parse_table(schema=True)
2675
2676        if self._match(TokenType.DEFAULT):
2677            expression: exp.Var | exp.PartitionBoundSpec = exp.var("DEFAULT")
2678        elif self._match_text_seq("FOR", "VALUES"):
2679            expression = self._parse_partition_bound_spec()
2680        else:
2681            self.raise_error("Expecting either DEFAULT or FOR VALUES clause.")
2682
2683        return self.expression(exp.PartitionedOfProperty, this=this, expression=expression)
2684
2685    def _parse_partitioned_by(self) -> exp.PartitionedByProperty:
2686        self._match(TokenType.EQ)
2687        return self.expression(
2688            exp.PartitionedByProperty,
2689            this=self._parse_schema() or self._parse_bracket(self._parse_field()),
2690        )
2691
2692    def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty:
2693        if self._match_text_seq("AND", "STATISTICS"):
2694            statistics = True
2695        elif self._match_text_seq("AND", "NO", "STATISTICS"):
2696            statistics = False
2697        else:
2698            statistics = None
2699
2700        return self.expression(exp.WithDataProperty, no=no, statistics=statistics)
2701
2702    def _parse_contains_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2703        if self._match_text_seq("SQL"):
2704            return self.expression(exp.SqlReadWriteProperty, this="CONTAINS SQL")
2705        return None
2706
2707    def _parse_modifies_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2708        if self._match_text_seq("SQL", "DATA"):
2709            return self.expression(exp.SqlReadWriteProperty, this="MODIFIES SQL DATA")
2710        return None
2711
2712    def _parse_no_property(self) -> t.Optional[exp.Expression]:
2713        if self._match_text_seq("PRIMARY", "INDEX"):
2714            return exp.NoPrimaryIndexProperty()
2715        if self._match_text_seq("SQL"):
2716            return self.expression(exp.SqlReadWriteProperty, this="NO SQL")
2717        return None
2718
2719    def _parse_on_property(self) -> t.Optional[exp.Expression]:
2720        if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"):
2721            return exp.OnCommitProperty()
2722        if self._match_text_seq("COMMIT", "DELETE", "ROWS"):
2723            return exp.OnCommitProperty(delete=True)
2724        return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var()))
2725
2726    def _parse_reads_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2727        if self._match_text_seq("SQL", "DATA"):
2728            return self.expression(exp.SqlReadWriteProperty, this="READS SQL DATA")
2729        return None
2730
2731    def _parse_distkey(self) -> exp.DistKeyProperty:
2732        return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var))
2733
2734    def _parse_create_like(self) -> t.Optional[exp.LikeProperty]:
2735        table = self._parse_table(schema=True)
2736
2737        options = []
2738        while self._match_texts(("INCLUDING", "EXCLUDING")):
2739            this = self._prev.text.upper()
2740
2741            id_var = self._parse_id_var()
2742            if not id_var:
2743                return None
2744
2745            options.append(
2746                self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper()))
2747            )
2748
2749        return self.expression(exp.LikeProperty, this=table, expressions=options)
2750
2751    def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty:
2752        return self.expression(
2753            exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound
2754        )
2755
2756    def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty:
2757        self._match(TokenType.EQ)
2758        return self.expression(
2759            exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default
2760        )
2761
2762    def _parse_remote_with_connection(self) -> exp.RemoteWithConnectionModelProperty:
2763        self._match_text_seq("WITH", "CONNECTION")
2764        return self.expression(
2765            exp.RemoteWithConnectionModelProperty, this=self._parse_table_parts()
2766        )
2767
2768    def _parse_returns(self) -> exp.ReturnsProperty:
2769        value: t.Optional[exp.Expression]
2770        null = None
2771        is_table = self._match(TokenType.TABLE)
2772
2773        if is_table:
2774            if self._match(TokenType.LT):
2775                value = self.expression(
2776                    exp.Schema,
2777                    this="TABLE",
2778                    expressions=self._parse_csv(self._parse_struct_types),
2779                )
2780                if not self._match(TokenType.GT):
2781                    self.raise_error("Expecting >")
2782            else:
2783                value = self._parse_schema(exp.var("TABLE"))
2784        elif self._match_text_seq("NULL", "ON", "NULL", "INPUT"):
2785            null = True
2786            value = None
2787        else:
2788            value = self._parse_types()
2789
2790        return self.expression(exp.ReturnsProperty, this=value, is_table=is_table, null=null)
2791
2792    def _parse_describe(self) -> exp.Describe:
2793        kind = self._match_set(self.CREATABLES) and self._prev.text
2794        style = self._match_texts(self.DESCRIBE_STYLES) and self._prev.text.upper()
2795        if self._match(TokenType.DOT):
2796            style = None
2797            self._retreat(self._index - 2)
2798
2799        format = self._parse_property() if self._match(TokenType.FORMAT, advance=False) else None
2800
2801        if self._match_set(self.STATEMENT_PARSERS, advance=False):
2802            this = self._parse_statement()
2803        else:
2804            this = self._parse_table(schema=True)
2805
2806        properties = self._parse_properties()
2807        expressions = properties.expressions if properties else None
2808        partition = self._parse_partition()
2809        return self.expression(
2810            exp.Describe,
2811            this=this,
2812            style=style,
2813            kind=kind,
2814            expressions=expressions,
2815            partition=partition,
2816            format=format,
2817        )
2818
2819    def _parse_multitable_inserts(self, comments: t.Optional[t.List[str]]) -> exp.MultitableInserts:
2820        kind = self._prev.text.upper()
2821        expressions = []
2822
2823        def parse_conditional_insert() -> t.Optional[exp.ConditionalInsert]:
2824            if self._match(TokenType.WHEN):
2825                expression = self._parse_disjunction()
2826                self._match(TokenType.THEN)
2827            else:
2828                expression = None
2829
2830            else_ = self._match(TokenType.ELSE)
2831
2832            if not self._match(TokenType.INTO):
2833                return None
2834
2835            return self.expression(
2836                exp.ConditionalInsert,
2837                this=self.expression(
2838                    exp.Insert,
2839                    this=self._parse_table(schema=True),
2840                    expression=self._parse_derived_table_values(),
2841                ),
2842                expression=expression,
2843                else_=else_,
2844            )
2845
2846        expression = parse_conditional_insert()
2847        while expression is not None:
2848            expressions.append(expression)
2849            expression = parse_conditional_insert()
2850
2851        return self.expression(
2852            exp.MultitableInserts,
2853            kind=kind,
2854            comments=comments,
2855            expressions=expressions,
2856            source=self._parse_table(),
2857        )
2858
2859    def _parse_insert(self) -> t.Union[exp.Insert, exp.MultitableInserts]:
2860        comments = []
2861        hint = self._parse_hint()
2862        overwrite = self._match(TokenType.OVERWRITE)
2863        ignore = self._match(TokenType.IGNORE)
2864        local = self._match_text_seq("LOCAL")
2865        alternative = None
2866        is_function = None
2867
2868        if self._match_text_seq("DIRECTORY"):
2869            this: t.Optional[exp.Expression] = self.expression(
2870                exp.Directory,
2871                this=self._parse_var_or_string(),
2872                local=local,
2873                row_format=self._parse_row_format(match_row=True),
2874            )
2875        else:
2876            if self._match_set((TokenType.FIRST, TokenType.ALL)):
2877                comments += ensure_list(self._prev_comments)
2878                return self._parse_multitable_inserts(comments)
2879
2880            if self._match(TokenType.OR):
2881                alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text
2882
2883            self._match(TokenType.INTO)
2884            comments += ensure_list(self._prev_comments)
2885            self._match(TokenType.TABLE)
2886            is_function = self._match(TokenType.FUNCTION)
2887
2888            this = (
2889                self._parse_table(schema=True, parse_partition=True)
2890                if not is_function
2891                else self._parse_function()
2892            )
2893            if isinstance(this, exp.Table) and self._match(TokenType.ALIAS, advance=False):
2894                this.set("alias", self._parse_table_alias())
2895
2896        returning = self._parse_returning()
2897
2898        return self.expression(
2899            exp.Insert,
2900            comments=comments,
2901            hint=hint,
2902            is_function=is_function,
2903            this=this,
2904            stored=self._match_text_seq("STORED") and self._parse_stored(),
2905            by_name=self._match_text_seq("BY", "NAME"),
2906            exists=self._parse_exists(),
2907            where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) and self._parse_assignment(),
2908            partition=self._match(TokenType.PARTITION_BY) and self._parse_partitioned_by(),
2909            settings=self._match_text_seq("SETTINGS") and self._parse_settings_property(),
2910            expression=self._parse_derived_table_values() or self._parse_ddl_select(),
2911            conflict=self._parse_on_conflict(),
2912            returning=returning or self._parse_returning(),
2913            overwrite=overwrite,
2914            alternative=alternative,
2915            ignore=ignore,
2916            source=self._match(TokenType.TABLE) and self._parse_table(),
2917        )
2918
2919    def _parse_kill(self) -> exp.Kill:
2920        kind = exp.var(self._prev.text) if self._match_texts(("CONNECTION", "QUERY")) else None
2921
2922        return self.expression(
2923            exp.Kill,
2924            this=self._parse_primary(),
2925            kind=kind,
2926        )
2927
2928    def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]:
2929        conflict = self._match_text_seq("ON", "CONFLICT")
2930        duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY")
2931
2932        if not conflict and not duplicate:
2933            return None
2934
2935        conflict_keys = None
2936        constraint = None
2937
2938        if conflict:
2939            if self._match_text_seq("ON", "CONSTRAINT"):
2940                constraint = self._parse_id_var()
2941            elif self._match(TokenType.L_PAREN):
2942                conflict_keys = self._parse_csv(self._parse_id_var)
2943                self._match_r_paren()
2944
2945        action = self._parse_var_from_options(self.CONFLICT_ACTIONS)
2946        if self._prev.token_type == TokenType.UPDATE:
2947            self._match(TokenType.SET)
2948            expressions = self._parse_csv(self._parse_equality)
2949        else:
2950            expressions = None
2951
2952        return self.expression(
2953            exp.OnConflict,
2954            duplicate=duplicate,
2955            expressions=expressions,
2956            action=action,
2957            conflict_keys=conflict_keys,
2958            constraint=constraint,
2959            where=self._parse_where(),
2960        )
2961
2962    def _parse_returning(self) -> t.Optional[exp.Returning]:
2963        if not self._match(TokenType.RETURNING):
2964            return None
2965        return self.expression(
2966            exp.Returning,
2967            expressions=self._parse_csv(self._parse_expression),
2968            into=self._match(TokenType.INTO) and self._parse_table_part(),
2969        )
2970
2971    def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2972        if not self._match(TokenType.FORMAT):
2973            return None
2974        return self._parse_row_format()
2975
2976    def _parse_serde_properties(self, with_: bool = False) -> t.Optional[exp.SerdeProperties]:
2977        index = self._index
2978        with_ = with_ or self._match_text_seq("WITH")
2979
2980        if not self._match(TokenType.SERDE_PROPERTIES):
2981            self._retreat(index)
2982            return None
2983        return self.expression(
2984            exp.SerdeProperties,
2985            **{  # type: ignore
2986                "expressions": self._parse_wrapped_properties(),
2987                "with": with_,
2988            },
2989        )
2990
2991    def _parse_row_format(
2992        self, match_row: bool = False
2993    ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2994        if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT):
2995            return None
2996
2997        if self._match_text_seq("SERDE"):
2998            this = self._parse_string()
2999
3000            serde_properties = self._parse_serde_properties()
3001
3002            return self.expression(
3003                exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties
3004            )
3005
3006        self._match_text_seq("DELIMITED")
3007
3008        kwargs = {}
3009
3010        if self._match_text_seq("FIELDS", "TERMINATED", "BY"):
3011            kwargs["fields"] = self._parse_string()
3012            if self._match_text_seq("ESCAPED", "BY"):
3013                kwargs["escaped"] = self._parse_string()
3014        if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"):
3015            kwargs["collection_items"] = self._parse_string()
3016        if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"):
3017            kwargs["map_keys"] = self._parse_string()
3018        if self._match_text_seq("LINES", "TERMINATED", "BY"):
3019            kwargs["lines"] = self._parse_string()
3020        if self._match_text_seq("NULL", "DEFINED", "AS"):
3021            kwargs["null"] = self._parse_string()
3022
3023        return self.expression(exp.RowFormatDelimitedProperty, **kwargs)  # type: ignore
3024
3025    def _parse_load(self) -> exp.LoadData | exp.Command:
3026        if self._match_text_seq("DATA"):
3027            local = self._match_text_seq("LOCAL")
3028            self._match_text_seq("INPATH")
3029            inpath = self._parse_string()
3030            overwrite = self._match(TokenType.OVERWRITE)
3031            self._match_pair(TokenType.INTO, TokenType.TABLE)
3032
3033            return self.expression(
3034                exp.LoadData,
3035                this=self._parse_table(schema=True),
3036                local=local,
3037                overwrite=overwrite,
3038                inpath=inpath,
3039                partition=self._parse_partition(),
3040                input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(),
3041                serde=self._match_text_seq("SERDE") and self._parse_string(),
3042            )
3043        return self._parse_as_command(self._prev)
3044
3045    def _parse_delete(self) -> exp.Delete:
3046        # This handles MySQL's "Multiple-Table Syntax"
3047        # https://dev.mysql.com/doc/refman/8.0/en/delete.html
3048        tables = None
3049        if not self._match(TokenType.FROM, advance=False):
3050            tables = self._parse_csv(self._parse_table) or None
3051
3052        returning = self._parse_returning()
3053
3054        return self.expression(
3055            exp.Delete,
3056            tables=tables,
3057            this=self._match(TokenType.FROM) and self._parse_table(joins=True),
3058            using=self._match(TokenType.USING) and self._parse_table(joins=True),
3059            cluster=self._match(TokenType.ON) and self._parse_on_property(),
3060            where=self._parse_where(),
3061            returning=returning or self._parse_returning(),
3062            limit=self._parse_limit(),
3063        )
3064
3065    def _parse_update(self) -> exp.Update:
3066        this = self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS)
3067        expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality)
3068        returning = self._parse_returning()
3069        return self.expression(
3070            exp.Update,
3071            **{  # type: ignore
3072                "this": this,
3073                "expressions": expressions,
3074                "from": self._parse_from(joins=True),
3075                "where": self._parse_where(),
3076                "returning": returning or self._parse_returning(),
3077                "order": self._parse_order(),
3078                "limit": self._parse_limit(),
3079            },
3080        )
3081
3082    def _parse_use(self) -> exp.Use:
3083        return self.expression(
3084            exp.Use,
3085            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
3086            this=self._parse_table(schema=False),
3087        )
3088
3089    def _parse_uncache(self) -> exp.Uncache:
3090        if not self._match(TokenType.TABLE):
3091            self.raise_error("Expecting TABLE after UNCACHE")
3092
3093        return self.expression(
3094            exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True)
3095        )
3096
3097    def _parse_cache(self) -> exp.Cache:
3098        lazy = self._match_text_seq("LAZY")
3099        self._match(TokenType.TABLE)
3100        table = self._parse_table(schema=True)
3101
3102        options = []
3103        if self._match_text_seq("OPTIONS"):
3104            self._match_l_paren()
3105            k = self._parse_string()
3106            self._match(TokenType.EQ)
3107            v = self._parse_string()
3108            options = [k, v]
3109            self._match_r_paren()
3110
3111        self._match(TokenType.ALIAS)
3112        return self.expression(
3113            exp.Cache,
3114            this=table,
3115            lazy=lazy,
3116            options=options,
3117            expression=self._parse_select(nested=True),
3118        )
3119
3120    def _parse_partition(self) -> t.Optional[exp.Partition]:
3121        if not self._match_texts(self.PARTITION_KEYWORDS):
3122            return None
3123
3124        return self.expression(
3125            exp.Partition,
3126            subpartition=self._prev.text.upper() == "SUBPARTITION",
3127            expressions=self._parse_wrapped_csv(self._parse_assignment),
3128        )
3129
3130    def _parse_value(self, values: bool = True) -> t.Optional[exp.Tuple]:
3131        def _parse_value_expression() -> t.Optional[exp.Expression]:
3132            if self.dialect.SUPPORTS_VALUES_DEFAULT and self._match(TokenType.DEFAULT):
3133                return exp.var(self._prev.text.upper())
3134            return self._parse_expression()
3135
3136        if self._match(TokenType.L_PAREN):
3137            expressions = self._parse_csv(_parse_value_expression)
3138            self._match_r_paren()
3139            return self.expression(exp.Tuple, expressions=expressions)
3140
3141        # In some dialects we can have VALUES 1, 2 which results in 1 column & 2 rows.
3142        expression = self._parse_expression()
3143        if expression:
3144            return self.expression(exp.Tuple, expressions=[expression])
3145        return None
3146
3147    def _parse_projections(self) -> t.List[exp.Expression]:
3148        return self._parse_expressions()
3149
3150    def _parse_wrapped_select(self, table: bool = False) -> t.Optional[exp.Expression]:
3151        if self._match_set((TokenType.PIVOT, TokenType.UNPIVOT)):
3152            this: t.Optional[exp.Expression] = self._parse_simplified_pivot(
3153                is_unpivot=self._prev.token_type == TokenType.UNPIVOT
3154            )
3155        elif self._match(TokenType.FROM):
3156            from_ = self._parse_from(skip_from_token=True, consume_pipe=True)
3157            # Support parentheses for duckdb FROM-first syntax
3158            select = self._parse_select()
3159            if select:
3160                select.set("from", from_)
3161                this = select
3162            else:
3163                this = exp.select("*").from_(t.cast(exp.From, from_))
3164        else:
3165            this = (
3166                self._parse_table(consume_pipe=True)
3167                if table
3168                else self._parse_select(nested=True, parse_set_operation=False)
3169            )
3170
3171            # Transform exp.Values into a exp.Table to pass through parse_query_modifiers
3172            # in case a modifier (e.g. join) is following
3173            if table and isinstance(this, exp.Values) and this.alias:
3174                alias = this.args["alias"].pop()
3175                this = exp.Table(this=this, alias=alias)
3176
3177            this = self._parse_query_modifiers(self._parse_set_operations(this))
3178
3179        return this
3180
3181    def _parse_select(
3182        self,
3183        nested: bool = False,
3184        table: bool = False,
3185        parse_subquery_alias: bool = True,
3186        parse_set_operation: bool = True,
3187        consume_pipe: bool = True,
3188    ) -> t.Optional[exp.Expression]:
3189        query = self._parse_select_query(
3190            nested=nested,
3191            table=table,
3192            parse_subquery_alias=parse_subquery_alias,
3193            parse_set_operation=parse_set_operation,
3194        )
3195
3196        if (
3197            consume_pipe
3198            and self._match(TokenType.PIPE_GT, advance=False)
3199            and isinstance(query, exp.Query)
3200        ):
3201            query = self._parse_pipe_syntax_query(query)
3202            query = query.subquery(copy=False) if query and table else query
3203
3204        return query
3205
3206    def _parse_select_query(
3207        self,
3208        nested: bool = False,
3209        table: bool = False,
3210        parse_subquery_alias: bool = True,
3211        parse_set_operation: bool = True,
3212    ) -> t.Optional[exp.Expression]:
3213        cte = self._parse_with()
3214
3215        if cte:
3216            this = self._parse_statement()
3217
3218            if not this:
3219                self.raise_error("Failed to parse any statement following CTE")
3220                return cte
3221
3222            if "with" in this.arg_types:
3223                this.set("with", cte)
3224            else:
3225                self.raise_error(f"{this.key} does not support CTE")
3226                this = cte
3227
3228            return this
3229
3230        # duckdb supports leading with FROM x
3231        from_ = (
3232            self._parse_from(consume_pipe=True)
3233            if self._match(TokenType.FROM, advance=False)
3234            else None
3235        )
3236
3237        if self._match(TokenType.SELECT):
3238            comments = self._prev_comments
3239
3240            hint = self._parse_hint()
3241
3242            if self._next and not self._next.token_type == TokenType.DOT:
3243                all_ = self._match(TokenType.ALL)
3244                distinct = self._match_set(self.DISTINCT_TOKENS)
3245            else:
3246                all_, distinct = None, None
3247
3248            kind = (
3249                self._match(TokenType.ALIAS)
3250                and self._match_texts(("STRUCT", "VALUE"))
3251                and self._prev.text.upper()
3252            )
3253
3254            if distinct:
3255                distinct = self.expression(
3256                    exp.Distinct,
3257                    on=self._parse_value(values=False) if self._match(TokenType.ON) else None,
3258                )
3259
3260            if all_ and distinct:
3261                self.raise_error("Cannot specify both ALL and DISTINCT after SELECT")
3262
3263            operation_modifiers = []
3264            while self._curr and self._match_texts(self.OPERATION_MODIFIERS):
3265                operation_modifiers.append(exp.var(self._prev.text.upper()))
3266
3267            limit = self._parse_limit(top=True)
3268            projections = self._parse_projections()
3269
3270            this = self.expression(
3271                exp.Select,
3272                kind=kind,
3273                hint=hint,
3274                distinct=distinct,
3275                expressions=projections,
3276                limit=limit,
3277                operation_modifiers=operation_modifiers or None,
3278            )
3279            this.comments = comments
3280
3281            into = self._parse_into()
3282            if into:
3283                this.set("into", into)
3284
3285            if not from_:
3286                from_ = self._parse_from()
3287
3288            if from_:
3289                this.set("from", from_)
3290
3291            this = self._parse_query_modifiers(this)
3292        elif (table or nested) and self._match(TokenType.L_PAREN):
3293            this = self._parse_wrapped_select(table=table)
3294
3295            # We return early here so that the UNION isn't attached to the subquery by the
3296            # following call to _parse_set_operations, but instead becomes the parent node
3297            self._match_r_paren()
3298            return self._parse_subquery(this, parse_alias=parse_subquery_alias)
3299        elif self._match(TokenType.VALUES, advance=False):
3300            this = self._parse_derived_table_values()
3301        elif from_:
3302            this = exp.select("*").from_(from_.this, copy=False)
3303        elif self._match(TokenType.SUMMARIZE):
3304            table = self._match(TokenType.TABLE)
3305            this = self._parse_select() or self._parse_string() or self._parse_table()
3306            return self.expression(exp.Summarize, this=this, table=table)
3307        elif self._match(TokenType.DESCRIBE):
3308            this = self._parse_describe()
3309        elif self._match_text_seq("STREAM"):
3310            this = self._parse_function()
3311            if this:
3312                this = self.expression(exp.Stream, this=this)
3313            else:
3314                self._retreat(self._index - 1)
3315        else:
3316            this = None
3317
3318        return self._parse_set_operations(this) if parse_set_operation else this
3319
3320    def _parse_recursive_with_search(self) -> t.Optional[exp.RecursiveWithSearch]:
3321        self._match_text_seq("SEARCH")
3322
3323        kind = self._match_texts(self.RECURSIVE_CTE_SEARCH_KIND) and self._prev.text.upper()
3324
3325        if not kind:
3326            return None
3327
3328        self._match_text_seq("FIRST", "BY")
3329
3330        return self.expression(
3331            exp.RecursiveWithSearch,
3332            kind=kind,
3333            this=self._parse_id_var(),
3334            expression=self._match_text_seq("SET") and self._parse_id_var(),
3335            using=self._match_text_seq("USING") and self._parse_id_var(),
3336        )
3337
3338    def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]:
3339        if not skip_with_token and not self._match(TokenType.WITH):
3340            return None
3341
3342        comments = self._prev_comments
3343        recursive = self._match(TokenType.RECURSIVE)
3344
3345        last_comments = None
3346        expressions = []
3347        while True:
3348            cte = self._parse_cte()
3349            if isinstance(cte, exp.CTE):
3350                expressions.append(cte)
3351                if last_comments:
3352                    cte.add_comments(last_comments)
3353
3354            if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH):
3355                break
3356            else:
3357                self._match(TokenType.WITH)
3358
3359            last_comments = self._prev_comments
3360
3361        return self.expression(
3362            exp.With,
3363            comments=comments,
3364            expressions=expressions,
3365            recursive=recursive,
3366            search=self._parse_recursive_with_search(),
3367        )
3368
3369    def _parse_cte(self) -> t.Optional[exp.CTE]:
3370        index = self._index
3371
3372        alias = self._parse_table_alias(self.ID_VAR_TOKENS)
3373        if not alias or not alias.this:
3374            self.raise_error("Expected CTE to have alias")
3375
3376        if not self._match(TokenType.ALIAS) and not self.OPTIONAL_ALIAS_TOKEN_CTE:
3377            self._retreat(index)
3378            return None
3379
3380        comments = self._prev_comments
3381
3382        if self._match_text_seq("NOT", "MATERIALIZED"):
3383            materialized = False
3384        elif self._match_text_seq("MATERIALIZED"):
3385            materialized = True
3386        else:
3387            materialized = None
3388
3389        cte = self.expression(
3390            exp.CTE,
3391            this=self._parse_wrapped(self._parse_statement),
3392            alias=alias,
3393            materialized=materialized,
3394            comments=comments,
3395        )
3396
3397        values = cte.this
3398        if isinstance(values, exp.Values):
3399            if values.alias:
3400                cte.set("this", exp.select("*").from_(values))
3401            else:
3402                cte.set("this", exp.select("*").from_(exp.alias_(values, "_values", table=True)))
3403
3404        return cte
3405
3406    def _parse_table_alias(
3407        self, alias_tokens: t.Optional[t.Collection[TokenType]] = None
3408    ) -> t.Optional[exp.TableAlias]:
3409        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
3410        # so this section tries to parse the clause version and if it fails, it treats the token
3411        # as an identifier (alias)
3412        if self._can_parse_limit_or_offset():
3413            return None
3414
3415        any_token = self._match(TokenType.ALIAS)
3416        alias = (
3417            self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3418            or self._parse_string_as_identifier()
3419        )
3420
3421        index = self._index
3422        if self._match(TokenType.L_PAREN):
3423            columns = self._parse_csv(self._parse_function_parameter)
3424            self._match_r_paren() if columns else self._retreat(index)
3425        else:
3426            columns = None
3427
3428        if not alias and not columns:
3429            return None
3430
3431        table_alias = self.expression(exp.TableAlias, this=alias, columns=columns)
3432
3433        # We bubble up comments from the Identifier to the TableAlias
3434        if isinstance(alias, exp.Identifier):
3435            table_alias.add_comments(alias.pop_comments())
3436
3437        return table_alias
3438
3439    def _parse_subquery(
3440        self, this: t.Optional[exp.Expression], parse_alias: bool = True
3441    ) -> t.Optional[exp.Subquery]:
3442        if not this:
3443            return None
3444
3445        return self.expression(
3446            exp.Subquery,
3447            this=this,
3448            pivots=self._parse_pivots(),
3449            alias=self._parse_table_alias() if parse_alias else None,
3450            sample=self._parse_table_sample(),
3451        )
3452
3453    def _implicit_unnests_to_explicit(self, this: E) -> E:
3454        from sqlglot.optimizer.normalize_identifiers import normalize_identifiers as _norm
3455
3456        refs = {_norm(this.args["from"].this.copy(), dialect=self.dialect).alias_or_name}
3457        for i, join in enumerate(this.args.get("joins") or []):
3458            table = join.this
3459            normalized_table = table.copy()
3460            normalized_table.meta["maybe_column"] = True
3461            normalized_table = _norm(normalized_table, dialect=self.dialect)
3462
3463            if isinstance(table, exp.Table) and not join.args.get("on"):
3464                if normalized_table.parts[0].name in refs:
3465                    table_as_column = table.to_column()
3466                    unnest = exp.Unnest(expressions=[table_as_column])
3467
3468                    # Table.to_column creates a parent Alias node that we want to convert to
3469                    # a TableAlias and attach to the Unnest, so it matches the parser's output
3470                    if isinstance(table.args.get("alias"), exp.TableAlias):
3471                        table_as_column.replace(table_as_column.this)
3472                        exp.alias_(unnest, None, table=[table.args["alias"].this], copy=False)
3473
3474                    table.replace(unnest)
3475
3476            refs.add(normalized_table.alias_or_name)
3477
3478        return this
3479
3480    def _parse_query_modifiers(
3481        self, this: t.Optional[exp.Expression]
3482    ) -> t.Optional[exp.Expression]:
3483        if isinstance(this, self.MODIFIABLES):
3484            for join in self._parse_joins():
3485                this.append("joins", join)
3486            for lateral in iter(self._parse_lateral, None):
3487                this.append("laterals", lateral)
3488
3489            while True:
3490                if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False):
3491                    parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type]
3492                    key, expression = parser(self)
3493
3494                    if expression:
3495                        this.set(key, expression)
3496                        if key == "limit":
3497                            offset = expression.args.pop("offset", None)
3498
3499                            if offset:
3500                                offset = exp.Offset(expression=offset)
3501                                this.set("offset", offset)
3502
3503                                limit_by_expressions = expression.expressions
3504                                expression.set("expressions", None)
3505                                offset.set("expressions", limit_by_expressions)
3506                        continue
3507                break
3508
3509        if self.SUPPORTS_IMPLICIT_UNNEST and this and this.args.get("from"):
3510            this = self._implicit_unnests_to_explicit(this)
3511
3512        return this
3513
3514    def _parse_hint_fallback_to_string(self) -> t.Optional[exp.Hint]:
3515        start = self._curr
3516        while self._curr:
3517            self._advance()
3518
3519        end = self._tokens[self._index - 1]
3520        return exp.Hint(expressions=[self._find_sql(start, end)])
3521
3522    def _parse_hint_function_call(self) -> t.Optional[exp.Expression]:
3523        return self._parse_function_call()
3524
3525    def _parse_hint_body(self) -> t.Optional[exp.Hint]:
3526        start_index = self._index
3527        should_fallback_to_string = False
3528
3529        hints = []
3530        try:
3531            for hint in iter(
3532                lambda: self._parse_csv(
3533                    lambda: self._parse_hint_function_call() or self._parse_var(upper=True),
3534                ),
3535                [],
3536            ):
3537                hints.extend(hint)
3538        except ParseError:
3539            should_fallback_to_string = True
3540
3541        if should_fallback_to_string or self._curr:
3542            self._retreat(start_index)
3543            return self._parse_hint_fallback_to_string()
3544
3545        return self.expression(exp.Hint, expressions=hints)
3546
3547    def _parse_hint(self) -> t.Optional[exp.Hint]:
3548        if self._match(TokenType.HINT) and self._prev_comments:
3549            return exp.maybe_parse(self._prev_comments[0], into=exp.Hint, dialect=self.dialect)
3550
3551        return None
3552
3553    def _parse_into(self) -> t.Optional[exp.Into]:
3554        if not self._match(TokenType.INTO):
3555            return None
3556
3557        temp = self._match(TokenType.TEMPORARY)
3558        unlogged = self._match_text_seq("UNLOGGED")
3559        self._match(TokenType.TABLE)
3560
3561        return self.expression(
3562            exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged
3563        )
3564
3565    def _parse_from(
3566        self,
3567        joins: bool = False,
3568        skip_from_token: bool = False,
3569        consume_pipe: bool = False,
3570    ) -> t.Optional[exp.From]:
3571        if not skip_from_token and not self._match(TokenType.FROM):
3572            return None
3573
3574        return self.expression(
3575            exp.From,
3576            comments=self._prev_comments,
3577            this=self._parse_table(joins=joins, consume_pipe=consume_pipe),
3578        )
3579
3580    def _parse_match_recognize_measure(self) -> exp.MatchRecognizeMeasure:
3581        return self.expression(
3582            exp.MatchRecognizeMeasure,
3583            window_frame=self._match_texts(("FINAL", "RUNNING")) and self._prev.text.upper(),
3584            this=self._parse_expression(),
3585        )
3586
3587    def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]:
3588        if not self._match(TokenType.MATCH_RECOGNIZE):
3589            return None
3590
3591        self._match_l_paren()
3592
3593        partition = self._parse_partition_by()
3594        order = self._parse_order()
3595
3596        measures = (
3597            self._parse_csv(self._parse_match_recognize_measure)
3598            if self._match_text_seq("MEASURES")
3599            else None
3600        )
3601
3602        if self._match_text_seq("ONE", "ROW", "PER", "MATCH"):
3603            rows = exp.var("ONE ROW PER MATCH")
3604        elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"):
3605            text = "ALL ROWS PER MATCH"
3606            if self._match_text_seq("SHOW", "EMPTY", "MATCHES"):
3607                text += " SHOW EMPTY MATCHES"
3608            elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"):
3609                text += " OMIT EMPTY MATCHES"
3610            elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"):
3611                text += " WITH UNMATCHED ROWS"
3612            rows = exp.var(text)
3613        else:
3614            rows = None
3615
3616        if self._match_text_seq("AFTER", "MATCH", "SKIP"):
3617            text = "AFTER MATCH SKIP"
3618            if self._match_text_seq("PAST", "LAST", "ROW"):
3619                text += " PAST LAST ROW"
3620            elif self._match_text_seq("TO", "NEXT", "ROW"):
3621                text += " TO NEXT ROW"
3622            elif self._match_text_seq("TO", "FIRST"):
3623                text += f" TO FIRST {self._advance_any().text}"  # type: ignore
3624            elif self._match_text_seq("TO", "LAST"):
3625                text += f" TO LAST {self._advance_any().text}"  # type: ignore
3626            after = exp.var(text)
3627        else:
3628            after = None
3629
3630        if self._match_text_seq("PATTERN"):
3631            self._match_l_paren()
3632
3633            if not self._curr:
3634                self.raise_error("Expecting )", self._curr)
3635
3636            paren = 1
3637            start = self._curr
3638
3639            while self._curr and paren > 0:
3640                if self._curr.token_type == TokenType.L_PAREN:
3641                    paren += 1
3642                if self._curr.token_type == TokenType.R_PAREN:
3643                    paren -= 1
3644
3645                end = self._prev
3646                self._advance()
3647
3648            if paren > 0:
3649                self.raise_error("Expecting )", self._curr)
3650
3651            pattern = exp.var(self._find_sql(start, end))
3652        else:
3653            pattern = None
3654
3655        define = (
3656            self._parse_csv(self._parse_name_as_expression)
3657            if self._match_text_seq("DEFINE")
3658            else None
3659        )
3660
3661        self._match_r_paren()
3662
3663        return self.expression(
3664            exp.MatchRecognize,
3665            partition_by=partition,
3666            order=order,
3667            measures=measures,
3668            rows=rows,
3669            after=after,
3670            pattern=pattern,
3671            define=define,
3672            alias=self._parse_table_alias(),
3673        )
3674
3675    def _parse_lateral(self) -> t.Optional[exp.Lateral]:
3676        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY)
3677        if not cross_apply and self._match_pair(TokenType.OUTER, TokenType.APPLY):
3678            cross_apply = False
3679
3680        if cross_apply is not None:
3681            this = self._parse_select(table=True)
3682            view = None
3683            outer = None
3684        elif self._match(TokenType.LATERAL):
3685            this = self._parse_select(table=True)
3686            view = self._match(TokenType.VIEW)
3687            outer = self._match(TokenType.OUTER)
3688        else:
3689            return None
3690
3691        if not this:
3692            this = (
3693                self._parse_unnest()
3694                or self._parse_function()
3695                or self._parse_id_var(any_token=False)
3696            )
3697
3698            while self._match(TokenType.DOT):
3699                this = exp.Dot(
3700                    this=this,
3701                    expression=self._parse_function() or self._parse_id_var(any_token=False),
3702                )
3703
3704        ordinality: t.Optional[bool] = None
3705
3706        if view:
3707            table = self._parse_id_var(any_token=False)
3708            columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else []
3709            table_alias: t.Optional[exp.TableAlias] = self.expression(
3710                exp.TableAlias, this=table, columns=columns
3711            )
3712        elif isinstance(this, (exp.Subquery, exp.Unnest)) and this.alias:
3713            # We move the alias from the lateral's child node to the lateral itself
3714            table_alias = this.args["alias"].pop()
3715        else:
3716            ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
3717            table_alias = self._parse_table_alias()
3718
3719        return self.expression(
3720            exp.Lateral,
3721            this=this,
3722            view=view,
3723            outer=outer,
3724            alias=table_alias,
3725            cross_apply=cross_apply,
3726            ordinality=ordinality,
3727        )
3728
3729    def _parse_join_parts(
3730        self,
3731    ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]:
3732        return (
3733            self._match_set(self.JOIN_METHODS) and self._prev,
3734            self._match_set(self.JOIN_SIDES) and self._prev,
3735            self._match_set(self.JOIN_KINDS) and self._prev,
3736        )
3737
3738    def _parse_using_identifiers(self) -> t.List[exp.Expression]:
3739        def _parse_column_as_identifier() -> t.Optional[exp.Expression]:
3740            this = self._parse_column()
3741            if isinstance(this, exp.Column):
3742                return this.this
3743            return this
3744
3745        return self._parse_wrapped_csv(_parse_column_as_identifier, optional=True)
3746
3747    def _parse_join(
3748        self, skip_join_token: bool = False, parse_bracket: bool = False
3749    ) -> t.Optional[exp.Join]:
3750        if self._match(TokenType.COMMA):
3751            table = self._try_parse(self._parse_table)
3752            cross_join = self.expression(exp.Join, this=table) if table else None
3753
3754            if cross_join and self.JOINS_HAVE_EQUAL_PRECEDENCE:
3755                cross_join.set("kind", "CROSS")
3756
3757            return cross_join
3758
3759        index = self._index
3760        method, side, kind = self._parse_join_parts()
3761        hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None
3762        join = self._match(TokenType.JOIN) or (kind and kind.token_type == TokenType.STRAIGHT_JOIN)
3763
3764        if not skip_join_token and not join:
3765            self._retreat(index)
3766            kind = None
3767            method = None
3768            side = None
3769
3770        outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False)
3771        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False)
3772
3773        if not skip_join_token and not join and not outer_apply and not cross_apply:
3774            return None
3775
3776        kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)}
3777        if kind and kind.token_type == TokenType.ARRAY and self._match(TokenType.COMMA):
3778            kwargs["expressions"] = self._parse_csv(
3779                lambda: self._parse_table(parse_bracket=parse_bracket)
3780            )
3781
3782        if method:
3783            kwargs["method"] = method.text
3784        if side:
3785            kwargs["side"] = side.text
3786        if kind:
3787            kwargs["kind"] = kind.text
3788        if hint:
3789            kwargs["hint"] = hint
3790
3791        if self._match(TokenType.MATCH_CONDITION):
3792            kwargs["match_condition"] = self._parse_wrapped(self._parse_comparison)
3793
3794        if self._match(TokenType.ON):
3795            kwargs["on"] = self._parse_assignment()
3796        elif self._match(TokenType.USING):
3797            kwargs["using"] = self._parse_using_identifiers()
3798        elif (
3799            not (outer_apply or cross_apply)
3800            and not isinstance(kwargs["this"], exp.Unnest)
3801            and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
3802        ):
3803            index = self._index
3804            joins: t.Optional[list] = list(self._parse_joins())
3805
3806            if joins and self._match(TokenType.ON):
3807                kwargs["on"] = self._parse_assignment()
3808            elif joins and self._match(TokenType.USING):
3809                kwargs["using"] = self._parse_using_identifiers()
3810            else:
3811                joins = None
3812                self._retreat(index)
3813
3814            kwargs["this"].set("joins", joins if joins else None)
3815
3816        kwargs["pivots"] = self._parse_pivots()
3817
3818        comments = [c for token in (method, side, kind) if token for c in token.comments]
3819        return self.expression(exp.Join, comments=comments, **kwargs)
3820
3821    def _parse_opclass(self) -> t.Optional[exp.Expression]:
3822        this = self._parse_assignment()
3823
3824        if self._match_texts(self.OPCLASS_FOLLOW_KEYWORDS, advance=False):
3825            return this
3826
3827        if not self._match_set(self.OPTYPE_FOLLOW_TOKENS, advance=False):
3828            return self.expression(exp.Opclass, this=this, expression=self._parse_table_parts())
3829
3830        return this
3831
3832    def _parse_index_params(self) -> exp.IndexParameters:
3833        using = self._parse_var(any_token=True) if self._match(TokenType.USING) else None
3834
3835        if self._match(TokenType.L_PAREN, advance=False):
3836            columns = self._parse_wrapped_csv(self._parse_with_operator)
3837        else:
3838            columns = None
3839
3840        include = self._parse_wrapped_id_vars() if self._match_text_seq("INCLUDE") else None
3841        partition_by = self._parse_partition_by()
3842        with_storage = self._match(TokenType.WITH) and self._parse_wrapped_properties()
3843        tablespace = (
3844            self._parse_var(any_token=True)
3845            if self._match_text_seq("USING", "INDEX", "TABLESPACE")
3846            else None
3847        )
3848        where = self._parse_where()
3849
3850        on = self._parse_field() if self._match(TokenType.ON) else None
3851
3852        return self.expression(
3853            exp.IndexParameters,
3854            using=using,
3855            columns=columns,
3856            include=include,
3857            partition_by=partition_by,
3858            where=where,
3859            with_storage=with_storage,
3860            tablespace=tablespace,
3861            on=on,
3862        )
3863
3864    def _parse_index(
3865        self, index: t.Optional[exp.Expression] = None, anonymous: bool = False
3866    ) -> t.Optional[exp.Index]:
3867        if index or anonymous:
3868            unique = None
3869            primary = None
3870            amp = None
3871
3872            self._match(TokenType.ON)
3873            self._match(TokenType.TABLE)  # hive
3874            table = self._parse_table_parts(schema=True)
3875        else:
3876            unique = self._match(TokenType.UNIQUE)
3877            primary = self._match_text_seq("PRIMARY")
3878            amp = self._match_text_seq("AMP")
3879
3880            if not self._match(TokenType.INDEX):
3881                return None
3882
3883            index = self._parse_id_var()
3884            table = None
3885
3886        params = self._parse_index_params()
3887
3888        return self.expression(
3889            exp.Index,
3890            this=index,
3891            table=table,
3892            unique=unique,
3893            primary=primary,
3894            amp=amp,
3895            params=params,
3896        )
3897
3898    def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]:
3899        hints: t.List[exp.Expression] = []
3900        if self._match_pair(TokenType.WITH, TokenType.L_PAREN):
3901            # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16
3902            hints.append(
3903                self.expression(
3904                    exp.WithTableHint,
3905                    expressions=self._parse_csv(
3906                        lambda: self._parse_function() or self._parse_var(any_token=True)
3907                    ),
3908                )
3909            )
3910            self._match_r_paren()
3911        else:
3912            # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html
3913            while self._match_set(self.TABLE_INDEX_HINT_TOKENS):
3914                hint = exp.IndexTableHint(this=self._prev.text.upper())
3915
3916                self._match_set((TokenType.INDEX, TokenType.KEY))
3917                if self._match(TokenType.FOR):
3918                    hint.set("target", self._advance_any() and self._prev.text.upper())
3919
3920                hint.set("expressions", self._parse_wrapped_id_vars())
3921                hints.append(hint)
3922
3923        return hints or None
3924
3925    def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]:
3926        return (
3927            (not schema and self._parse_function(optional_parens=False))
3928            or self._parse_id_var(any_token=False)
3929            or self._parse_string_as_identifier()
3930            or self._parse_placeholder()
3931        )
3932
3933    def _parse_table_parts(
3934        self, schema: bool = False, is_db_reference: bool = False, wildcard: bool = False
3935    ) -> exp.Table:
3936        catalog = None
3937        db = None
3938        table: t.Optional[exp.Expression | str] = self._parse_table_part(schema=schema)
3939
3940        while self._match(TokenType.DOT):
3941            if catalog:
3942                # This allows nesting the table in arbitrarily many dot expressions if needed
3943                table = self.expression(
3944                    exp.Dot, this=table, expression=self._parse_table_part(schema=schema)
3945                )
3946            else:
3947                catalog = db
3948                db = table
3949                # "" used for tsql FROM a..b case
3950                table = self._parse_table_part(schema=schema) or ""
3951
3952        if (
3953            wildcard
3954            and self._is_connected()
3955            and (isinstance(table, exp.Identifier) or not table)
3956            and self._match(TokenType.STAR)
3957        ):
3958            if isinstance(table, exp.Identifier):
3959                table.args["this"] += "*"
3960            else:
3961                table = exp.Identifier(this="*")
3962
3963        # We bubble up comments from the Identifier to the Table
3964        comments = table.pop_comments() if isinstance(table, exp.Expression) else None
3965
3966        if is_db_reference:
3967            catalog = db
3968            db = table
3969            table = None
3970
3971        if not table and not is_db_reference:
3972            self.raise_error(f"Expected table name but got {self._curr}")
3973        if not db and is_db_reference:
3974            self.raise_error(f"Expected database name but got {self._curr}")
3975
3976        table = self.expression(
3977            exp.Table,
3978            comments=comments,
3979            this=table,
3980            db=db,
3981            catalog=catalog,
3982        )
3983
3984        changes = self._parse_changes()
3985        if changes:
3986            table.set("changes", changes)
3987
3988        at_before = self._parse_historical_data()
3989        if at_before:
3990            table.set("when", at_before)
3991
3992        pivots = self._parse_pivots()
3993        if pivots:
3994            table.set("pivots", pivots)
3995
3996        return table
3997
3998    def _parse_table(
3999        self,
4000        schema: bool = False,
4001        joins: bool = False,
4002        alias_tokens: t.Optional[t.Collection[TokenType]] = None,
4003        parse_bracket: bool = False,
4004        is_db_reference: bool = False,
4005        parse_partition: bool = False,
4006        consume_pipe: bool = False,
4007    ) -> t.Optional[exp.Expression]:
4008        lateral = self._parse_lateral()
4009        if lateral:
4010            return lateral
4011
4012        unnest = self._parse_unnest()
4013        if unnest:
4014            return unnest
4015
4016        values = self._parse_derived_table_values()
4017        if values:
4018            return values
4019
4020        subquery = self._parse_select(table=True, consume_pipe=consume_pipe)
4021        if subquery:
4022            if not subquery.args.get("pivots"):
4023                subquery.set("pivots", self._parse_pivots())
4024            return subquery
4025
4026        bracket = parse_bracket and self._parse_bracket(None)
4027        bracket = self.expression(exp.Table, this=bracket) if bracket else None
4028
4029        rows_from = self._match_text_seq("ROWS", "FROM") and self._parse_wrapped_csv(
4030            self._parse_table
4031        )
4032        rows_from = self.expression(exp.Table, rows_from=rows_from) if rows_from else None
4033
4034        only = self._match(TokenType.ONLY)
4035
4036        this = t.cast(
4037            exp.Expression,
4038            bracket
4039            or rows_from
4040            or self._parse_bracket(
4041                self._parse_table_parts(schema=schema, is_db_reference=is_db_reference)
4042            ),
4043        )
4044
4045        if only:
4046            this.set("only", only)
4047
4048        # Postgres supports a wildcard (table) suffix operator, which is a no-op in this context
4049        self._match_text_seq("*")
4050
4051        parse_partition = parse_partition or self.SUPPORTS_PARTITION_SELECTION
4052        if parse_partition and self._match(TokenType.PARTITION, advance=False):
4053            this.set("partition", self._parse_partition())
4054
4055        if schema:
4056            return self._parse_schema(this=this)
4057
4058        version = self._parse_version()
4059
4060        if version:
4061            this.set("version", version)
4062
4063        if self.dialect.ALIAS_POST_TABLESAMPLE:
4064            this.set("sample", self._parse_table_sample())
4065
4066        alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
4067        if alias:
4068            this.set("alias", alias)
4069
4070        if isinstance(this, exp.Table) and self._match_text_seq("AT"):
4071            return self.expression(
4072                exp.AtIndex, this=this.to_column(copy=False), expression=self._parse_id_var()
4073            )
4074
4075        this.set("hints", self._parse_table_hints())
4076
4077        if not this.args.get("pivots"):
4078            this.set("pivots", self._parse_pivots())
4079
4080        if not self.dialect.ALIAS_POST_TABLESAMPLE:
4081            this.set("sample", self._parse_table_sample())
4082
4083        if joins:
4084            for join in self._parse_joins():
4085                this.append("joins", join)
4086
4087        if self._match_pair(TokenType.WITH, TokenType.ORDINALITY):
4088            this.set("ordinality", True)
4089            this.set("alias", self._parse_table_alias())
4090
4091        return this
4092
4093    def _parse_version(self) -> t.Optional[exp.Version]:
4094        if self._match(TokenType.TIMESTAMP_SNAPSHOT):
4095            this = "TIMESTAMP"
4096        elif self._match(TokenType.VERSION_SNAPSHOT):
4097            this = "VERSION"
4098        else:
4099            return None
4100
4101        if self._match_set((TokenType.FROM, TokenType.BETWEEN)):
4102            kind = self._prev.text.upper()
4103            start = self._parse_bitwise()
4104            self._match_texts(("TO", "AND"))
4105            end = self._parse_bitwise()
4106            expression: t.Optional[exp.Expression] = self.expression(
4107                exp.Tuple, expressions=[start, end]
4108            )
4109        elif self._match_text_seq("CONTAINED", "IN"):
4110            kind = "CONTAINED IN"
4111            expression = self.expression(
4112                exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise)
4113            )
4114        elif self._match(TokenType.ALL):
4115            kind = "ALL"
4116            expression = None
4117        else:
4118            self._match_text_seq("AS", "OF")
4119            kind = "AS OF"
4120            expression = self._parse_type()
4121
4122        return self.expression(exp.Version, this=this, expression=expression, kind=kind)
4123
4124    def _parse_historical_data(self) -> t.Optional[exp.HistoricalData]:
4125        # https://docs.snowflake.com/en/sql-reference/constructs/at-before
4126        index = self._index
4127        historical_data = None
4128        if self._match_texts(self.HISTORICAL_DATA_PREFIX):
4129            this = self._prev.text.upper()
4130            kind = (
4131                self._match(TokenType.L_PAREN)
4132                and self._match_texts(self.HISTORICAL_DATA_KIND)
4133                and self._prev.text.upper()
4134            )
4135            expression = self._match(TokenType.FARROW) and self._parse_bitwise()
4136
4137            if expression:
4138                self._match_r_paren()
4139                historical_data = self.expression(
4140                    exp.HistoricalData, this=this, kind=kind, expression=expression
4141                )
4142            else:
4143                self._retreat(index)
4144
4145        return historical_data
4146
4147    def _parse_changes(self) -> t.Optional[exp.Changes]:
4148        if not self._match_text_seq("CHANGES", "(", "INFORMATION", "=>"):
4149            return None
4150
4151        information = self._parse_var(any_token=True)
4152        self._match_r_paren()
4153
4154        return self.expression(
4155            exp.Changes,
4156            information=information,
4157            at_before=self._parse_historical_data(),
4158            end=self._parse_historical_data(),
4159        )
4160
4161    def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]:
4162        if not self._match(TokenType.UNNEST):
4163            return None
4164
4165        expressions = self._parse_wrapped_csv(self._parse_equality)
4166        offset = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
4167
4168        alias = self._parse_table_alias() if with_alias else None
4169
4170        if alias:
4171            if self.dialect.UNNEST_COLUMN_ONLY:
4172                if alias.args.get("columns"):
4173                    self.raise_error("Unexpected extra column alias in unnest.")
4174
4175                alias.set("columns", [alias.this])
4176                alias.set("this", None)
4177
4178            columns = alias.args.get("columns") or []
4179            if offset and len(expressions) < len(columns):
4180                offset = columns.pop()
4181
4182        if not offset and self._match_pair(TokenType.WITH, TokenType.OFFSET):
4183            self._match(TokenType.ALIAS)
4184            offset = self._parse_id_var(
4185                any_token=False, tokens=self.UNNEST_OFFSET_ALIAS_TOKENS
4186            ) or exp.to_identifier("offset")
4187
4188        return self.expression(exp.Unnest, expressions=expressions, alias=alias, offset=offset)
4189
4190    def _parse_derived_table_values(self) -> t.Optional[exp.Values]:
4191        is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES)
4192        if not is_derived and not (
4193            # ClickHouse's `FORMAT Values` is equivalent to `VALUES`
4194            self._match_text_seq("VALUES") or self._match_text_seq("FORMAT", "VALUES")
4195        ):
4196            return None
4197
4198        expressions = self._parse_csv(self._parse_value)
4199        alias = self._parse_table_alias()
4200
4201        if is_derived:
4202            self._match_r_paren()
4203
4204        return self.expression(
4205            exp.Values, expressions=expressions, alias=alias or self._parse_table_alias()
4206        )
4207
4208    def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]:
4209        if not self._match(TokenType.TABLE_SAMPLE) and not (
4210            as_modifier and self._match_text_seq("USING", "SAMPLE")
4211        ):
4212            return None
4213
4214        bucket_numerator = None
4215        bucket_denominator = None
4216        bucket_field = None
4217        percent = None
4218        size = None
4219        seed = None
4220
4221        method = self._parse_var(tokens=(TokenType.ROW,), upper=True)
4222        matched_l_paren = self._match(TokenType.L_PAREN)
4223
4224        if self.TABLESAMPLE_CSV:
4225            num = None
4226            expressions = self._parse_csv(self._parse_primary)
4227        else:
4228            expressions = None
4229            num = (
4230                self._parse_factor()
4231                if self._match(TokenType.NUMBER, advance=False)
4232                else self._parse_primary() or self._parse_placeholder()
4233            )
4234
4235        if self._match_text_seq("BUCKET"):
4236            bucket_numerator = self._parse_number()
4237            self._match_text_seq("OUT", "OF")
4238            bucket_denominator = bucket_denominator = self._parse_number()
4239            self._match(TokenType.ON)
4240            bucket_field = self._parse_field()
4241        elif self._match_set((TokenType.PERCENT, TokenType.MOD)):
4242            percent = num
4243        elif self._match(TokenType.ROWS) or not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
4244            size = num
4245        else:
4246            percent = num
4247
4248        if matched_l_paren:
4249            self._match_r_paren()
4250
4251        if self._match(TokenType.L_PAREN):
4252            method = self._parse_var(upper=True)
4253            seed = self._match(TokenType.COMMA) and self._parse_number()
4254            self._match_r_paren()
4255        elif self._match_texts(("SEED", "REPEATABLE")):
4256            seed = self._parse_wrapped(self._parse_number)
4257
4258        if not method and self.DEFAULT_SAMPLING_METHOD:
4259            method = exp.var(self.DEFAULT_SAMPLING_METHOD)
4260
4261        return self.expression(
4262            exp.TableSample,
4263            expressions=expressions,
4264            method=method,
4265            bucket_numerator=bucket_numerator,
4266            bucket_denominator=bucket_denominator,
4267            bucket_field=bucket_field,
4268            percent=percent,
4269            size=size,
4270            seed=seed,
4271        )
4272
4273    def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]:
4274        return list(iter(self._parse_pivot, None)) or None
4275
4276    def _parse_joins(self) -> t.Iterator[exp.Join]:
4277        return iter(self._parse_join, None)
4278
4279    def _parse_unpivot_columns(self) -> t.Optional[exp.UnpivotColumns]:
4280        if not self._match(TokenType.INTO):
4281            return None
4282
4283        return self.expression(
4284            exp.UnpivotColumns,
4285            this=self._match_text_seq("NAME") and self._parse_column(),
4286            expressions=self._match_text_seq("VALUE") and self._parse_csv(self._parse_column),
4287        )
4288
4289    # https://duckdb.org/docs/sql/statements/pivot
4290    def _parse_simplified_pivot(self, is_unpivot: t.Optional[bool] = None) -> exp.Pivot:
4291        def _parse_on() -> t.Optional[exp.Expression]:
4292            this = self._parse_bitwise()
4293
4294            if self._match(TokenType.IN):
4295                # PIVOT ... ON col IN (row_val1, row_val2)
4296                return self._parse_in(this)
4297            if self._match(TokenType.ALIAS, advance=False):
4298                # UNPIVOT ... ON (col1, col2, col3) AS row_val
4299                return self._parse_alias(this)
4300
4301            return this
4302
4303        this = self._parse_table()
4304        expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on)
4305        into = self._parse_unpivot_columns()
4306        using = self._match(TokenType.USING) and self._parse_csv(
4307            lambda: self._parse_alias(self._parse_function())
4308        )
4309        group = self._parse_group()
4310
4311        return self.expression(
4312            exp.Pivot,
4313            this=this,
4314            expressions=expressions,
4315            using=using,
4316            group=group,
4317            unpivot=is_unpivot,
4318            into=into,
4319        )
4320
4321    def _parse_pivot_in(self) -> exp.In:
4322        def _parse_aliased_expression() -> t.Optional[exp.Expression]:
4323            this = self._parse_select_or_expression()
4324
4325            self._match(TokenType.ALIAS)
4326            alias = self._parse_bitwise()
4327            if alias:
4328                if isinstance(alias, exp.Column) and not alias.db:
4329                    alias = alias.this
4330                return self.expression(exp.PivotAlias, this=this, alias=alias)
4331
4332            return this
4333
4334        value = self._parse_column()
4335
4336        if not self._match_pair(TokenType.IN, TokenType.L_PAREN):
4337            self.raise_error("Expecting IN (")
4338
4339        if self._match(TokenType.ANY):
4340            exprs: t.List[exp.Expression] = ensure_list(exp.PivotAny(this=self._parse_order()))
4341        else:
4342            exprs = self._parse_csv(_parse_aliased_expression)
4343
4344        self._match_r_paren()
4345        return self.expression(exp.In, this=value, expressions=exprs)
4346
4347    def _parse_pivot(self) -> t.Optional[exp.Pivot]:
4348        index = self._index
4349        include_nulls = None
4350
4351        if self._match(TokenType.PIVOT):
4352            unpivot = False
4353        elif self._match(TokenType.UNPIVOT):
4354            unpivot = True
4355
4356            # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax
4357            if self._match_text_seq("INCLUDE", "NULLS"):
4358                include_nulls = True
4359            elif self._match_text_seq("EXCLUDE", "NULLS"):
4360                include_nulls = False
4361        else:
4362            return None
4363
4364        expressions = []
4365
4366        if not self._match(TokenType.L_PAREN):
4367            self._retreat(index)
4368            return None
4369
4370        if unpivot:
4371            expressions = self._parse_csv(self._parse_column)
4372        else:
4373            expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function()))
4374
4375        if not expressions:
4376            self.raise_error("Failed to parse PIVOT's aggregation list")
4377
4378        if not self._match(TokenType.FOR):
4379            self.raise_error("Expecting FOR")
4380
4381        fields = []
4382        while True:
4383            field = self._try_parse(self._parse_pivot_in)
4384            if not field:
4385                break
4386            fields.append(field)
4387
4388        default_on_null = self._match_text_seq("DEFAULT", "ON", "NULL") and self._parse_wrapped(
4389            self._parse_bitwise
4390        )
4391
4392        group = self._parse_group()
4393
4394        self._match_r_paren()
4395
4396        pivot = self.expression(
4397            exp.Pivot,
4398            expressions=expressions,
4399            fields=fields,
4400            unpivot=unpivot,
4401            include_nulls=include_nulls,
4402            default_on_null=default_on_null,
4403            group=group,
4404        )
4405
4406        if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False):
4407            pivot.set("alias", self._parse_table_alias())
4408
4409        if not unpivot:
4410            names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions))
4411
4412            columns: t.List[exp.Expression] = []
4413            all_fields = []
4414            for pivot_field in pivot.fields:
4415                pivot_field_expressions = pivot_field.expressions
4416
4417                # The `PivotAny` expression corresponds to `ANY ORDER BY <column>`; we can't infer in this case.
4418                if isinstance(seq_get(pivot_field_expressions, 0), exp.PivotAny):
4419                    continue
4420
4421                all_fields.append(
4422                    [
4423                        fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name
4424                        for fld in pivot_field_expressions
4425                    ]
4426                )
4427
4428            if all_fields:
4429                if names:
4430                    all_fields.append(names)
4431
4432                # Generate all possible combinations of the pivot columns
4433                # e.g PIVOT(sum(...) as total FOR year IN (2000, 2010) FOR country IN ('NL', 'US'))
4434                # generates the product between [[2000, 2010], ['NL', 'US'], ['total']]
4435                for fld_parts_tuple in itertools.product(*all_fields):
4436                    fld_parts = list(fld_parts_tuple)
4437
4438                    if names and self.PREFIXED_PIVOT_COLUMNS:
4439                        # Move the "name" to the front of the list
4440                        fld_parts.insert(0, fld_parts.pop(-1))
4441
4442                    columns.append(exp.to_identifier("_".join(fld_parts)))
4443
4444            pivot.set("columns", columns)
4445
4446        return pivot
4447
4448    def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]:
4449        return [agg.alias for agg in aggregations if agg.alias]
4450
4451    def _parse_prewhere(self, skip_where_token: bool = False) -> t.Optional[exp.PreWhere]:
4452        if not skip_where_token and not self._match(TokenType.PREWHERE):
4453            return None
4454
4455        return self.expression(
4456            exp.PreWhere, comments=self._prev_comments, this=self._parse_assignment()
4457        )
4458
4459    def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]:
4460        if not skip_where_token and not self._match(TokenType.WHERE):
4461            return None
4462
4463        return self.expression(
4464            exp.Where, comments=self._prev_comments, this=self._parse_assignment()
4465        )
4466
4467    def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]:
4468        if not skip_group_by_token and not self._match(TokenType.GROUP_BY):
4469            return None
4470
4471        elements: t.Dict[str, t.Any] = defaultdict(list)
4472
4473        if self._match(TokenType.ALL):
4474            elements["all"] = True
4475        elif self._match(TokenType.DISTINCT):
4476            elements["all"] = False
4477
4478        while True:
4479            index = self._index
4480
4481            elements["expressions"].extend(
4482                self._parse_csv(
4483                    lambda: None
4484                    if self._match_set((TokenType.CUBE, TokenType.ROLLUP), advance=False)
4485                    else self._parse_assignment()
4486                )
4487            )
4488
4489            before_with_index = self._index
4490            with_prefix = self._match(TokenType.WITH)
4491
4492            if self._match(TokenType.ROLLUP):
4493                elements["rollup"].append(
4494                    self._parse_cube_or_rollup(exp.Rollup, with_prefix=with_prefix)
4495                )
4496            elif self._match(TokenType.CUBE):
4497                elements["cube"].append(
4498                    self._parse_cube_or_rollup(exp.Cube, with_prefix=with_prefix)
4499                )
4500            elif self._match(TokenType.GROUPING_SETS):
4501                elements["grouping_sets"].append(
4502                    self.expression(
4503                        exp.GroupingSets,
4504                        expressions=self._parse_wrapped_csv(self._parse_grouping_set),
4505                    )
4506                )
4507            elif self._match_text_seq("TOTALS"):
4508                elements["totals"] = True  # type: ignore
4509
4510            if before_with_index <= self._index <= before_with_index + 1:
4511                self._retreat(before_with_index)
4512                break
4513
4514            if index == self._index:
4515                break
4516
4517        return self.expression(exp.Group, **elements)  # type: ignore
4518
4519    def _parse_cube_or_rollup(self, kind: t.Type[E], with_prefix: bool = False) -> E:
4520        return self.expression(
4521            kind, expressions=[] if with_prefix else self._parse_wrapped_csv(self._parse_column)
4522        )
4523
4524    def _parse_grouping_set(self) -> t.Optional[exp.Expression]:
4525        if self._match(TokenType.L_PAREN):
4526            grouping_set = self._parse_csv(self._parse_column)
4527            self._match_r_paren()
4528            return self.expression(exp.Tuple, expressions=grouping_set)
4529
4530        return self._parse_column()
4531
4532    def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]:
4533        if not skip_having_token and not self._match(TokenType.HAVING):
4534            return None
4535        return self.expression(exp.Having, this=self._parse_assignment())
4536
4537    def _parse_qualify(self) -> t.Optional[exp.Qualify]:
4538        if not self._match(TokenType.QUALIFY):
4539            return None
4540        return self.expression(exp.Qualify, this=self._parse_assignment())
4541
4542    def _parse_connect_with_prior(self) -> t.Optional[exp.Expression]:
4543        self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression(
4544            exp.Prior, this=self._parse_bitwise()
4545        )
4546        connect = self._parse_assignment()
4547        self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR")
4548        return connect
4549
4550    def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]:
4551        if skip_start_token:
4552            start = None
4553        elif self._match(TokenType.START_WITH):
4554            start = self._parse_assignment()
4555        else:
4556            return None
4557
4558        self._match(TokenType.CONNECT_BY)
4559        nocycle = self._match_text_seq("NOCYCLE")
4560        connect = self._parse_connect_with_prior()
4561
4562        if not start and self._match(TokenType.START_WITH):
4563            start = self._parse_assignment()
4564
4565        return self.expression(exp.Connect, start=start, connect=connect, nocycle=nocycle)
4566
4567    def _parse_name_as_expression(self) -> t.Optional[exp.Expression]:
4568        this = self._parse_id_var(any_token=True)
4569        if self._match(TokenType.ALIAS):
4570            this = self.expression(exp.Alias, alias=this, this=self._parse_assignment())
4571        return this
4572
4573    def _parse_interpolate(self) -> t.Optional[t.List[exp.Expression]]:
4574        if self._match_text_seq("INTERPOLATE"):
4575            return self._parse_wrapped_csv(self._parse_name_as_expression)
4576        return None
4577
4578    def _parse_order(
4579        self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False
4580    ) -> t.Optional[exp.Expression]:
4581        siblings = None
4582        if not skip_order_token and not self._match(TokenType.ORDER_BY):
4583            if not self._match(TokenType.ORDER_SIBLINGS_BY):
4584                return this
4585
4586            siblings = True
4587
4588        return self.expression(
4589            exp.Order,
4590            this=this,
4591            expressions=self._parse_csv(self._parse_ordered),
4592            siblings=siblings,
4593        )
4594
4595    def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]:
4596        if not self._match(token):
4597            return None
4598        return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered))
4599
4600    def _parse_ordered(
4601        self, parse_method: t.Optional[t.Callable] = None
4602    ) -> t.Optional[exp.Ordered]:
4603        this = parse_method() if parse_method else self._parse_assignment()
4604        if not this:
4605            return None
4606
4607        if this.name.upper() == "ALL" and self.dialect.SUPPORTS_ORDER_BY_ALL:
4608            this = exp.var("ALL")
4609
4610        asc = self._match(TokenType.ASC)
4611        desc = self._match(TokenType.DESC) or (asc and False)
4612
4613        is_nulls_first = self._match_text_seq("NULLS", "FIRST")
4614        is_nulls_last = self._match_text_seq("NULLS", "LAST")
4615
4616        nulls_first = is_nulls_first or False
4617        explicitly_null_ordered = is_nulls_first or is_nulls_last
4618
4619        if (
4620            not explicitly_null_ordered
4621            and (
4622                (not desc and self.dialect.NULL_ORDERING == "nulls_are_small")
4623                or (desc and self.dialect.NULL_ORDERING != "nulls_are_small")
4624            )
4625            and self.dialect.NULL_ORDERING != "nulls_are_last"
4626        ):
4627            nulls_first = True
4628
4629        if self._match_text_seq("WITH", "FILL"):
4630            with_fill = self.expression(
4631                exp.WithFill,
4632                **{  # type: ignore
4633                    "from": self._match(TokenType.FROM) and self._parse_bitwise(),
4634                    "to": self._match_text_seq("TO") and self._parse_bitwise(),
4635                    "step": self._match_text_seq("STEP") and self._parse_bitwise(),
4636                    "interpolate": self._parse_interpolate(),
4637                },
4638            )
4639        else:
4640            with_fill = None
4641
4642        return self.expression(
4643            exp.Ordered, this=this, desc=desc, nulls_first=nulls_first, with_fill=with_fill
4644        )
4645
4646    def _parse_limit_options(self) -> exp.LimitOptions:
4647        percent = self._match(TokenType.PERCENT)
4648        rows = self._match_set((TokenType.ROW, TokenType.ROWS))
4649        self._match_text_seq("ONLY")
4650        with_ties = self._match_text_seq("WITH", "TIES")
4651        return self.expression(exp.LimitOptions, percent=percent, rows=rows, with_ties=with_ties)
4652
4653    def _parse_limit(
4654        self,
4655        this: t.Optional[exp.Expression] = None,
4656        top: bool = False,
4657        skip_limit_token: bool = False,
4658    ) -> t.Optional[exp.Expression]:
4659        if skip_limit_token or self._match(TokenType.TOP if top else TokenType.LIMIT):
4660            comments = self._prev_comments
4661            if top:
4662                limit_paren = self._match(TokenType.L_PAREN)
4663                expression = self._parse_term() if limit_paren else self._parse_number()
4664
4665                if limit_paren:
4666                    self._match_r_paren()
4667
4668                limit_options = self._parse_limit_options()
4669            else:
4670                limit_options = None
4671                expression = self._parse_term()
4672
4673            if self._match(TokenType.COMMA):
4674                offset = expression
4675                expression = self._parse_term()
4676            else:
4677                offset = None
4678
4679            limit_exp = self.expression(
4680                exp.Limit,
4681                this=this,
4682                expression=expression,
4683                offset=offset,
4684                comments=comments,
4685                limit_options=limit_options,
4686                expressions=self._parse_limit_by(),
4687            )
4688
4689            return limit_exp
4690
4691        if self._match(TokenType.FETCH):
4692            direction = self._match_set((TokenType.FIRST, TokenType.NEXT))
4693            direction = self._prev.text.upper() if direction else "FIRST"
4694
4695            count = self._parse_field(tokens=self.FETCH_TOKENS)
4696
4697            return self.expression(
4698                exp.Fetch,
4699                direction=direction,
4700                count=count,
4701                limit_options=self._parse_limit_options(),
4702            )
4703
4704        return this
4705
4706    def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4707        if not self._match(TokenType.OFFSET):
4708            return this
4709
4710        count = self._parse_term()
4711        self._match_set((TokenType.ROW, TokenType.ROWS))
4712
4713        return self.expression(
4714            exp.Offset, this=this, expression=count, expressions=self._parse_limit_by()
4715        )
4716
4717    def _can_parse_limit_or_offset(self) -> bool:
4718        if not self._match_set(self.AMBIGUOUS_ALIAS_TOKENS, advance=False):
4719            return False
4720
4721        index = self._index
4722        result = bool(
4723            self._try_parse(self._parse_limit, retreat=True)
4724            or self._try_parse(self._parse_offset, retreat=True)
4725        )
4726        self._retreat(index)
4727        return result
4728
4729    def _parse_limit_by(self) -> t.Optional[t.List[exp.Expression]]:
4730        return self._match_text_seq("BY") and self._parse_csv(self._parse_bitwise)
4731
4732    def _parse_locks(self) -> t.List[exp.Lock]:
4733        locks = []
4734        while True:
4735            if self._match_text_seq("FOR", "UPDATE"):
4736                update = True
4737            elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq(
4738                "LOCK", "IN", "SHARE", "MODE"
4739            ):
4740                update = False
4741            else:
4742                break
4743
4744            expressions = None
4745            if self._match_text_seq("OF"):
4746                expressions = self._parse_csv(lambda: self._parse_table(schema=True))
4747
4748            wait: t.Optional[bool | exp.Expression] = None
4749            if self._match_text_seq("NOWAIT"):
4750                wait = True
4751            elif self._match_text_seq("WAIT"):
4752                wait = self._parse_primary()
4753            elif self._match_text_seq("SKIP", "LOCKED"):
4754                wait = False
4755
4756            locks.append(
4757                self.expression(exp.Lock, update=update, expressions=expressions, wait=wait)
4758            )
4759
4760        return locks
4761
4762    def parse_set_operation(
4763        self, this: t.Optional[exp.Expression], consume_pipe: bool = False
4764    ) -> t.Optional[exp.Expression]:
4765        start = self._index
4766        _, side_token, kind_token = self._parse_join_parts()
4767
4768        side = side_token.text if side_token else None
4769        kind = kind_token.text if kind_token else None
4770
4771        if not self._match_set(self.SET_OPERATIONS):
4772            self._retreat(start)
4773            return None
4774
4775        token_type = self._prev.token_type
4776
4777        if token_type == TokenType.UNION:
4778            operation: t.Type[exp.SetOperation] = exp.Union
4779        elif token_type == TokenType.EXCEPT:
4780            operation = exp.Except
4781        else:
4782            operation = exp.Intersect
4783
4784        comments = self._prev.comments
4785
4786        if self._match(TokenType.DISTINCT):
4787            distinct: t.Optional[bool] = True
4788        elif self._match(TokenType.ALL):
4789            distinct = False
4790        else:
4791            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4792            if distinct is None:
4793                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4794
4795        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4796            "STRICT", "CORRESPONDING"
4797        )
4798        if self._match_text_seq("CORRESPONDING"):
4799            by_name = True
4800            if not side and not kind:
4801                kind = "INNER"
4802
4803        on_column_list = None
4804        if by_name and self._match_texts(("ON", "BY")):
4805            on_column_list = self._parse_wrapped_csv(self._parse_column)
4806
4807        expression = self._parse_select(
4808            nested=True, parse_set_operation=False, consume_pipe=consume_pipe
4809        )
4810
4811        return self.expression(
4812            operation,
4813            comments=comments,
4814            this=this,
4815            distinct=distinct,
4816            by_name=by_name,
4817            expression=expression,
4818            side=side,
4819            kind=kind,
4820            on=on_column_list,
4821        )
4822
4823    def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4824        while this:
4825            setop = self.parse_set_operation(this)
4826            if not setop:
4827                break
4828            this = setop
4829
4830        if isinstance(this, exp.SetOperation) and self.MODIFIERS_ATTACHED_TO_SET_OP:
4831            expression = this.expression
4832
4833            if expression:
4834                for arg in self.SET_OP_MODIFIERS:
4835                    expr = expression.args.get(arg)
4836                    if expr:
4837                        this.set(arg, expr.pop())
4838
4839        return this
4840
4841    def _parse_expression(self) -> t.Optional[exp.Expression]:
4842        return self._parse_alias(self._parse_assignment())
4843
4844    def _parse_assignment(self) -> t.Optional[exp.Expression]:
4845        this = self._parse_disjunction()
4846        if not this and self._next and self._next.token_type in self.ASSIGNMENT:
4847            # This allows us to parse <non-identifier token> := <expr>
4848            this = exp.column(
4849                t.cast(str, self._advance_any(ignore_reserved=True) and self._prev.text)
4850            )
4851
4852        while self._match_set(self.ASSIGNMENT):
4853            if isinstance(this, exp.Column) and len(this.parts) == 1:
4854                this = this.this
4855
4856            this = self.expression(
4857                self.ASSIGNMENT[self._prev.token_type],
4858                this=this,
4859                comments=self._prev_comments,
4860                expression=self._parse_assignment(),
4861            )
4862
4863        return this
4864
4865    def _parse_disjunction(self) -> t.Optional[exp.Expression]:
4866        return self._parse_tokens(self._parse_conjunction, self.DISJUNCTION)
4867
4868    def _parse_conjunction(self) -> t.Optional[exp.Expression]:
4869        return self._parse_tokens(self._parse_equality, self.CONJUNCTION)
4870
4871    def _parse_equality(self) -> t.Optional[exp.Expression]:
4872        return self._parse_tokens(self._parse_comparison, self.EQUALITY)
4873
4874    def _parse_comparison(self) -> t.Optional[exp.Expression]:
4875        return self._parse_tokens(self._parse_range, self.COMPARISON)
4876
4877    def _parse_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4878        this = this or self._parse_bitwise()
4879        negate = self._match(TokenType.NOT)
4880
4881        if self._match_set(self.RANGE_PARSERS):
4882            expression = self.RANGE_PARSERS[self._prev.token_type](self, this)
4883            if not expression:
4884                return this
4885
4886            this = expression
4887        elif self._match(TokenType.ISNULL):
4888            this = self.expression(exp.Is, this=this, expression=exp.Null())
4889
4890        # Postgres supports ISNULL and NOTNULL for conditions.
4891        # https://blog.andreiavram.ro/postgresql-null-composite-type/
4892        if self._match(TokenType.NOTNULL):
4893            this = self.expression(exp.Is, this=this, expression=exp.Null())
4894            this = self.expression(exp.Not, this=this)
4895
4896        if negate:
4897            this = self._negate_range(this)
4898
4899        if self._match(TokenType.IS):
4900            this = self._parse_is(this)
4901
4902        return this
4903
4904    def _negate_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4905        if not this:
4906            return this
4907
4908        return self.expression(exp.Not, this=this)
4909
4910    def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4911        index = self._index - 1
4912        negate = self._match(TokenType.NOT)
4913
4914        if self._match_text_seq("DISTINCT", "FROM"):
4915            klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ
4916            return self.expression(klass, this=this, expression=self._parse_bitwise())
4917
4918        if self._match(TokenType.JSON):
4919            kind = self._match_texts(self.IS_JSON_PREDICATE_KIND) and self._prev.text.upper()
4920
4921            if self._match_text_seq("WITH"):
4922                _with = True
4923            elif self._match_text_seq("WITHOUT"):
4924                _with = False
4925            else:
4926                _with = None
4927
4928            unique = self._match(TokenType.UNIQUE)
4929            self._match_text_seq("KEYS")
4930            expression: t.Optional[exp.Expression] = self.expression(
4931                exp.JSON, **{"this": kind, "with": _with, "unique": unique}
4932            )
4933        else:
4934            expression = self._parse_primary() or self._parse_null()
4935            if not expression:
4936                self._retreat(index)
4937                return None
4938
4939        this = self.expression(exp.Is, this=this, expression=expression)
4940        return self.expression(exp.Not, this=this) if negate else this
4941
4942    def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In:
4943        unnest = self._parse_unnest(with_alias=False)
4944        if unnest:
4945            this = self.expression(exp.In, this=this, unnest=unnest)
4946        elif self._match_set((TokenType.L_PAREN, TokenType.L_BRACKET)):
4947            matched_l_paren = self._prev.token_type == TokenType.L_PAREN
4948            expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias))
4949
4950            if len(expressions) == 1 and isinstance(expressions[0], exp.Query):
4951                this = self.expression(exp.In, this=this, query=expressions[0].subquery(copy=False))
4952            else:
4953                this = self.expression(exp.In, this=this, expressions=expressions)
4954
4955            if matched_l_paren:
4956                self._match_r_paren(this)
4957            elif not self._match(TokenType.R_BRACKET, expression=this):
4958                self.raise_error("Expecting ]")
4959        else:
4960            this = self.expression(exp.In, this=this, field=self._parse_column())
4961
4962        return this
4963
4964    def _parse_between(self, this: t.Optional[exp.Expression]) -> exp.Between:
4965        low = self._parse_bitwise()
4966        self._match(TokenType.AND)
4967        high = self._parse_bitwise()
4968        return self.expression(exp.Between, this=this, low=low, high=high)
4969
4970    def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4971        if not self._match(TokenType.ESCAPE):
4972            return this
4973        return self.expression(exp.Escape, this=this, expression=self._parse_string())
4974
4975    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Add | exp.Interval]:
4976        index = self._index
4977
4978        if not self._match(TokenType.INTERVAL) and match_interval:
4979            return None
4980
4981        if self._match(TokenType.STRING, advance=False):
4982            this = self._parse_primary()
4983        else:
4984            this = self._parse_term()
4985
4986        if not this or (
4987            isinstance(this, exp.Column)
4988            and not this.table
4989            and not this.this.quoted
4990            and this.name.upper() == "IS"
4991        ):
4992            self._retreat(index)
4993            return None
4994
4995        unit = self._parse_function() or (
4996            not self._match(TokenType.ALIAS, advance=False)
4997            and self._parse_var(any_token=True, upper=True)
4998        )
4999
5000        # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse
5001        # each INTERVAL expression into this canonical form so it's easy to transpile
5002        if this and this.is_number:
5003            this = exp.Literal.string(this.to_py())
5004        elif this and this.is_string:
5005            parts = exp.INTERVAL_STRING_RE.findall(this.name)
5006            if parts and unit:
5007                # Unconsume the eagerly-parsed unit, since the real unit was part of the string
5008                unit = None
5009                self._retreat(self._index - 1)
5010
5011            if len(parts) == 1:
5012                this = exp.Literal.string(parts[0][0])
5013                unit = self.expression(exp.Var, this=parts[0][1].upper())
5014        if self.INTERVAL_SPANS and self._match_text_seq("TO"):
5015            unit = self.expression(
5016                exp.IntervalSpan, this=unit, expression=self._parse_var(any_token=True, upper=True)
5017            )
5018
5019        interval = self.expression(exp.Interval, this=this, unit=unit)
5020
5021        index = self._index
5022        self._match(TokenType.PLUS)
5023
5024        # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
5025        if self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
5026            return self.expression(
5027                exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
5028            )
5029
5030        self._retreat(index)
5031        return interval
5032
5033    def _parse_bitwise(self) -> t.Optional[exp.Expression]:
5034        this = self._parse_term()
5035
5036        while True:
5037            if self._match_set(self.BITWISE):
5038                this = self.expression(
5039                    self.BITWISE[self._prev.token_type],
5040                    this=this,
5041                    expression=self._parse_term(),
5042                )
5043            elif self.dialect.DPIPE_IS_STRING_CONCAT and self._match(TokenType.DPIPE):
5044                this = self.expression(
5045                    exp.DPipe,
5046                    this=this,
5047                    expression=self._parse_term(),
5048                    safe=not self.dialect.STRICT_STRING_CONCAT,
5049                )
5050            elif self._match(TokenType.DQMARK):
5051                this = self.expression(
5052                    exp.Coalesce, this=this, expressions=ensure_list(self._parse_term())
5053                )
5054            elif self._match_pair(TokenType.LT, TokenType.LT):
5055                this = self.expression(
5056                    exp.BitwiseLeftShift, this=this, expression=self._parse_term()
5057                )
5058            elif self._match_pair(TokenType.GT, TokenType.GT):
5059                this = self.expression(
5060                    exp.BitwiseRightShift, this=this, expression=self._parse_term()
5061                )
5062            else:
5063                break
5064
5065        return this
5066
5067    def _parse_term(self) -> t.Optional[exp.Expression]:
5068        this = self._parse_factor()
5069
5070        while self._match_set(self.TERM):
5071            klass = self.TERM[self._prev.token_type]
5072            comments = self._prev_comments
5073            expression = self._parse_factor()
5074
5075            this = self.expression(klass, this=this, comments=comments, expression=expression)
5076
5077            if isinstance(this, exp.Collate):
5078                expr = this.expression
5079
5080                # Preserve collations such as pg_catalog."default" (Postgres) as columns, otherwise
5081                # fallback to Identifier / Var
5082                if isinstance(expr, exp.Column) and len(expr.parts) == 1:
5083                    ident = expr.this
5084                    if isinstance(ident, exp.Identifier):
5085                        this.set("expression", ident if ident.quoted else exp.var(ident.name))
5086
5087        return this
5088
5089    def _parse_factor(self) -> t.Optional[exp.Expression]:
5090        parse_method = self._parse_exponent if self.EXPONENT else self._parse_unary
5091        this = parse_method()
5092
5093        while self._match_set(self.FACTOR):
5094            klass = self.FACTOR[self._prev.token_type]
5095            comments = self._prev_comments
5096            expression = parse_method()
5097
5098            if not expression and klass is exp.IntDiv and self._prev.text.isalpha():
5099                self._retreat(self._index - 1)
5100                return this
5101
5102            this = self.expression(klass, this=this, comments=comments, expression=expression)
5103
5104            if isinstance(this, exp.Div):
5105                this.args["typed"] = self.dialect.TYPED_DIVISION
5106                this.args["safe"] = self.dialect.SAFE_DIVISION
5107
5108        return this
5109
5110    def _parse_exponent(self) -> t.Optional[exp.Expression]:
5111        return self._parse_tokens(self._parse_unary, self.EXPONENT)
5112
5113    def _parse_unary(self) -> t.Optional[exp.Expression]:
5114        if self._match_set(self.UNARY_PARSERS):
5115            return self.UNARY_PARSERS[self._prev.token_type](self)
5116        return self._parse_at_time_zone(self._parse_type())
5117
5118    def _parse_type(
5119        self, parse_interval: bool = True, fallback_to_identifier: bool = False
5120    ) -> t.Optional[exp.Expression]:
5121        interval = parse_interval and self._parse_interval()
5122        if interval:
5123            return interval
5124
5125        index = self._index
5126        data_type = self._parse_types(check_func=True, allow_identifiers=False)
5127
5128        # parse_types() returns a Cast if we parsed BQ's inline constructor <type>(<values>) e.g.
5129        # STRUCT<a INT, b STRING>(1, 'foo'), which is canonicalized to CAST(<values> AS <type>)
5130        if isinstance(data_type, exp.Cast):
5131            # This constructor can contain ops directly after it, for instance struct unnesting:
5132            # STRUCT<a INT, b STRING>(1, 'foo').* --> CAST(STRUCT(1, 'foo') AS STRUCT<a iNT, b STRING).*
5133            return self._parse_column_ops(data_type)
5134
5135        if data_type:
5136            index2 = self._index
5137            this = self._parse_primary()
5138
5139            if isinstance(this, exp.Literal):
5140                literal = this.name
5141                this = self._parse_column_ops(this)
5142
5143                parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
5144                if parser:
5145                    return parser(self, this, data_type)
5146
5147                if (
5148                    self.ZONE_AWARE_TIMESTAMP_CONSTRUCTOR
5149                    and data_type.is_type(exp.DataType.Type.TIMESTAMP)
5150                    and TIME_ZONE_RE.search(literal)
5151                ):
5152                    data_type = exp.DataType.build("TIMESTAMPTZ")
5153
5154                return self.expression(exp.Cast, this=this, to=data_type)
5155
5156            # The expressions arg gets set by the parser when we have something like DECIMAL(38, 0)
5157            # in the input SQL. In that case, we'll produce these tokens: DECIMAL ( 38 , 0 )
5158            #
5159            # If the index difference here is greater than 1, that means the parser itself must have
5160            # consumed additional tokens such as the DECIMAL scale and precision in the above example.
5161            #
5162            # If it's not greater than 1, then it must be 1, because we've consumed at least the type
5163            # keyword, meaning that the expressions arg of the DataType must have gotten set by a
5164            # callable in the TYPE_CONVERTERS mapping. For example, Snowflake converts DECIMAL to
5165            # DECIMAL(38, 0)) in order to facilitate the data type's transpilation.
5166            #
5167            # In these cases, we don't really want to return the converted type, but instead retreat
5168            # and try to parse a Column or Identifier in the section below.
5169            if data_type.expressions and index2 - index > 1:
5170                self._retreat(index2)
5171                return self._parse_column_ops(data_type)
5172
5173            self._retreat(index)
5174
5175        if fallback_to_identifier:
5176            return self._parse_id_var()
5177
5178        this = self._parse_column()
5179        return this and self._parse_column_ops(this)
5180
5181    def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]:
5182        this = self._parse_type()
5183        if not this:
5184            return None
5185
5186        if isinstance(this, exp.Column) and not this.table:
5187            this = exp.var(this.name.upper())
5188
5189        return self.expression(
5190            exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True)
5191        )
5192
5193    def _parse_user_defined_type(self, identifier: exp.Identifier) -> t.Optional[exp.Expression]:
5194        type_name = identifier.name
5195
5196        while self._match(TokenType.DOT):
5197            type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
5198
5199        return exp.DataType.build(type_name, udt=True)
5200
5201    def _parse_types(
5202        self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
5203    ) -> t.Optional[exp.Expression]:
5204        index = self._index
5205
5206        this: t.Optional[exp.Expression] = None
5207        prefix = self._match_text_seq("SYSUDTLIB", ".")
5208
5209        if not self._match_set(self.TYPE_TOKENS):
5210            identifier = allow_identifiers and self._parse_id_var(
5211                any_token=False, tokens=(TokenType.VAR,)
5212            )
5213            if isinstance(identifier, exp.Identifier):
5214                tokens = self.dialect.tokenize(identifier.sql(dialect=self.dialect))
5215
5216                if len(tokens) != 1:
5217                    self.raise_error("Unexpected identifier", self._prev)
5218
5219                if tokens[0].token_type in self.TYPE_TOKENS:
5220                    self._prev = tokens[0]
5221                elif self.dialect.SUPPORTS_USER_DEFINED_TYPES:
5222                    this = self._parse_user_defined_type(identifier)
5223                else:
5224                    self._retreat(self._index - 1)
5225                    return None
5226            else:
5227                return None
5228
5229        type_token = self._prev.token_type
5230
5231        if type_token == TokenType.PSEUDO_TYPE:
5232            return self.expression(exp.PseudoType, this=self._prev.text.upper())
5233
5234        if type_token == TokenType.OBJECT_IDENTIFIER:
5235            return self.expression(exp.ObjectIdentifier, this=self._prev.text.upper())
5236
5237        # https://materialize.com/docs/sql/types/map/
5238        if type_token == TokenType.MAP and self._match(TokenType.L_BRACKET):
5239            key_type = self._parse_types(
5240                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5241            )
5242            if not self._match(TokenType.FARROW):
5243                self._retreat(index)
5244                return None
5245
5246            value_type = self._parse_types(
5247                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5248            )
5249            if not self._match(TokenType.R_BRACKET):
5250                self._retreat(index)
5251                return None
5252
5253            return exp.DataType(
5254                this=exp.DataType.Type.MAP,
5255                expressions=[key_type, value_type],
5256                nested=True,
5257                prefix=prefix,
5258            )
5259
5260        nested = type_token in self.NESTED_TYPE_TOKENS
5261        is_struct = type_token in self.STRUCT_TYPE_TOKENS
5262        is_aggregate = type_token in self.AGGREGATE_TYPE_TOKENS
5263        expressions = None
5264        maybe_func = False
5265
5266        if self._match(TokenType.L_PAREN):
5267            if is_struct:
5268                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5269            elif nested:
5270                expressions = self._parse_csv(
5271                    lambda: self._parse_types(
5272                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5273                    )
5274                )
5275                if type_token == TokenType.NULLABLE and len(expressions) == 1:
5276                    this = expressions[0]
5277                    this.set("nullable", True)
5278                    self._match_r_paren()
5279                    return this
5280            elif type_token in self.ENUM_TYPE_TOKENS:
5281                expressions = self._parse_csv(self._parse_equality)
5282            elif is_aggregate:
5283                func_or_ident = self._parse_function(anonymous=True) or self._parse_id_var(
5284                    any_token=False, tokens=(TokenType.VAR, TokenType.ANY)
5285                )
5286                if not func_or_ident:
5287                    return None
5288                expressions = [func_or_ident]
5289                if self._match(TokenType.COMMA):
5290                    expressions.extend(
5291                        self._parse_csv(
5292                            lambda: self._parse_types(
5293                                check_func=check_func,
5294                                schema=schema,
5295                                allow_identifiers=allow_identifiers,
5296                            )
5297                        )
5298                    )
5299            else:
5300                expressions = self._parse_csv(self._parse_type_size)
5301
5302                # https://docs.snowflake.com/en/sql-reference/data-types-vector
5303                if type_token == TokenType.VECTOR and len(expressions) == 2:
5304                    expressions[0] = exp.DataType.build(expressions[0].name, dialect=self.dialect)
5305
5306            if not expressions or not self._match(TokenType.R_PAREN):
5307                self._retreat(index)
5308                return None
5309
5310            maybe_func = True
5311
5312        values: t.Optional[t.List[exp.Expression]] = None
5313
5314        if nested and self._match(TokenType.LT):
5315            if is_struct:
5316                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5317            else:
5318                expressions = self._parse_csv(
5319                    lambda: self._parse_types(
5320                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5321                    )
5322                )
5323
5324            if not self._match(TokenType.GT):
5325                self.raise_error("Expecting >")
5326
5327            if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)):
5328                values = self._parse_csv(self._parse_assignment)
5329                if not values and is_struct:
5330                    values = None
5331                    self._retreat(self._index - 1)
5332                else:
5333                    self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN))
5334
5335        if type_token in self.TIMESTAMPS:
5336            if self._match_text_seq("WITH", "TIME", "ZONE"):
5337                maybe_func = False
5338                tz_type = (
5339                    exp.DataType.Type.TIMETZ
5340                    if type_token in self.TIMES
5341                    else exp.DataType.Type.TIMESTAMPTZ
5342                )
5343                this = exp.DataType(this=tz_type, expressions=expressions)
5344            elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"):
5345                maybe_func = False
5346                this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions)
5347            elif self._match_text_seq("WITHOUT", "TIME", "ZONE"):
5348                maybe_func = False
5349        elif type_token == TokenType.INTERVAL:
5350            unit = self._parse_var(upper=True)
5351            if unit:
5352                if self._match_text_seq("TO"):
5353                    unit = exp.IntervalSpan(this=unit, expression=self._parse_var(upper=True))
5354
5355                this = self.expression(exp.DataType, this=self.expression(exp.Interval, unit=unit))
5356            else:
5357                this = self.expression(exp.DataType, this=exp.DataType.Type.INTERVAL)
5358        elif type_token == TokenType.VOID:
5359            this = exp.DataType(this=exp.DataType.Type.NULL)
5360
5361        if maybe_func and check_func:
5362            index2 = self._index
5363            peek = self._parse_string()
5364
5365            if not peek:
5366                self._retreat(index)
5367                return None
5368
5369            self._retreat(index2)
5370
5371        if not this:
5372            if self._match_text_seq("UNSIGNED"):
5373                unsigned_type_token = self.SIGNED_TO_UNSIGNED_TYPE_TOKEN.get(type_token)
5374                if not unsigned_type_token:
5375                    self.raise_error(f"Cannot convert {type_token.value} to unsigned.")
5376
5377                type_token = unsigned_type_token or type_token
5378
5379            this = exp.DataType(
5380                this=exp.DataType.Type[type_token.value],
5381                expressions=expressions,
5382                nested=nested,
5383                prefix=prefix,
5384            )
5385
5386            # Empty arrays/structs are allowed
5387            if values is not None:
5388                cls = exp.Struct if is_struct else exp.Array
5389                this = exp.cast(cls(expressions=values), this, copy=False)
5390
5391        elif expressions:
5392            this.set("expressions", expressions)
5393
5394        # https://materialize.com/docs/sql/types/list/#type-name
5395        while self._match(TokenType.LIST):
5396            this = exp.DataType(this=exp.DataType.Type.LIST, expressions=[this], nested=True)
5397
5398        index = self._index
5399
5400        # Postgres supports the INT ARRAY[3] syntax as a synonym for INT[3]
5401        matched_array = self._match(TokenType.ARRAY)
5402
5403        while self._curr:
5404            datatype_token = self._prev.token_type
5405            matched_l_bracket = self._match(TokenType.L_BRACKET)
5406
5407            if (not matched_l_bracket and not matched_array) or (
5408                datatype_token == TokenType.ARRAY and self._match(TokenType.R_BRACKET)
5409            ):
5410                # Postgres allows casting empty arrays such as ARRAY[]::INT[],
5411                # not to be confused with the fixed size array parsing
5412                break
5413
5414            matched_array = False
5415            values = self._parse_csv(self._parse_assignment) or None
5416            if (
5417                values
5418                and not schema
5419                and (
5420                    not self.dialect.SUPPORTS_FIXED_SIZE_ARRAYS or datatype_token == TokenType.ARRAY
5421                )
5422            ):
5423                # Retreating here means that we should not parse the following values as part of the data type, e.g. in DuckDB
5424                # 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
5425                self._retreat(index)
5426                break
5427
5428            this = exp.DataType(
5429                this=exp.DataType.Type.ARRAY, expressions=[this], values=values, nested=True
5430            )
5431            self._match(TokenType.R_BRACKET)
5432
5433        if self.TYPE_CONVERTERS and isinstance(this.this, exp.DataType.Type):
5434            converter = self.TYPE_CONVERTERS.get(this.this)
5435            if converter:
5436                this = converter(t.cast(exp.DataType, this))
5437
5438        return this
5439
5440    def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]:
5441        index = self._index
5442
5443        if (
5444            self._curr
5445            and self._next
5446            and self._curr.token_type in self.TYPE_TOKENS
5447            and self._next.token_type in self.TYPE_TOKENS
5448        ):
5449            # Takes care of special cases like `STRUCT<list ARRAY<...>>` where the identifier is also a
5450            # type token. Without this, the list will be parsed as a type and we'll eventually crash
5451            this = self._parse_id_var()
5452        else:
5453            this = (
5454                self._parse_type(parse_interval=False, fallback_to_identifier=True)
5455                or self._parse_id_var()
5456            )
5457
5458        self._match(TokenType.COLON)
5459
5460        if (
5461            type_required
5462            and not isinstance(this, exp.DataType)
5463            and not self._match_set(self.TYPE_TOKENS, advance=False)
5464        ):
5465            self._retreat(index)
5466            return self._parse_types()
5467
5468        return self._parse_column_def(this)
5469
5470    def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5471        if not self._match_text_seq("AT", "TIME", "ZONE"):
5472            return this
5473        return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary())
5474
5475    def _parse_column(self) -> t.Optional[exp.Expression]:
5476        this = self._parse_column_reference()
5477        column = self._parse_column_ops(this) if this else self._parse_bracket(this)
5478
5479        if self.dialect.SUPPORTS_COLUMN_JOIN_MARKS and column:
5480            column.set("join_mark", self._match(TokenType.JOIN_MARKER))
5481
5482        return column
5483
5484    def _parse_column_reference(self) -> t.Optional[exp.Expression]:
5485        this = self._parse_field()
5486        if (
5487            not this
5488            and self._match(TokenType.VALUES, advance=False)
5489            and self.VALUES_FOLLOWED_BY_PAREN
5490            and (not self._next or self._next.token_type != TokenType.L_PAREN)
5491        ):
5492            this = self._parse_id_var()
5493
5494        if isinstance(this, exp.Identifier):
5495            # We bubble up comments from the Identifier to the Column
5496            this = self.expression(exp.Column, comments=this.pop_comments(), this=this)
5497
5498        return this
5499
5500    def _parse_colon_as_variant_extract(
5501        self, this: t.Optional[exp.Expression]
5502    ) -> t.Optional[exp.Expression]:
5503        casts = []
5504        json_path = []
5505        escape = None
5506
5507        while self._match(TokenType.COLON):
5508            start_index = self._index
5509
5510            # Snowflake allows reserved keywords as json keys but advance_any() excludes TokenType.SELECT from any_tokens=True
5511            path = self._parse_column_ops(
5512                self._parse_field(any_token=True, tokens=(TokenType.SELECT,))
5513            )
5514
5515            # The cast :: operator has a lower precedence than the extraction operator :, so
5516            # we rearrange the AST appropriately to avoid casting the JSON path
5517            while isinstance(path, exp.Cast):
5518                casts.append(path.to)
5519                path = path.this
5520
5521            if casts:
5522                dcolon_offset = next(
5523                    i
5524                    for i, t in enumerate(self._tokens[start_index:])
5525                    if t.token_type == TokenType.DCOLON
5526                )
5527                end_token = self._tokens[start_index + dcolon_offset - 1]
5528            else:
5529                end_token = self._prev
5530
5531            if path:
5532                # Escape single quotes from Snowflake's colon extraction (e.g. col:"a'b") as
5533                # it'll roundtrip to a string literal in GET_PATH
5534                if isinstance(path, exp.Identifier) and path.quoted:
5535                    escape = True
5536
5537                json_path.append(self._find_sql(self._tokens[start_index], end_token))
5538
5539        # The VARIANT extract in Snowflake/Databricks is parsed as a JSONExtract; Snowflake uses the json_path in GET_PATH() while
5540        # Databricks transforms it back to the colon/dot notation
5541        if json_path:
5542            json_path_expr = self.dialect.to_json_path(exp.Literal.string(".".join(json_path)))
5543
5544            if json_path_expr:
5545                json_path_expr.set("escape", escape)
5546
5547            this = self.expression(
5548                exp.JSONExtract,
5549                this=this,
5550                expression=json_path_expr,
5551                variant_extract=True,
5552            )
5553
5554            while casts:
5555                this = self.expression(exp.Cast, this=this, to=casts.pop())
5556
5557        return this
5558
5559    def _parse_dcolon(self) -> t.Optional[exp.Expression]:
5560        return self._parse_types()
5561
5562    def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5563        this = self._parse_bracket(this)
5564
5565        while self._match_set(self.COLUMN_OPERATORS):
5566            op_token = self._prev.token_type
5567            op = self.COLUMN_OPERATORS.get(op_token)
5568
5569            if op_token in (TokenType.DCOLON, TokenType.DOTCOLON):
5570                field = self._parse_dcolon()
5571                if not field:
5572                    self.raise_error("Expected type")
5573            elif op and self._curr:
5574                field = self._parse_column_reference() or self._parse_bracket()
5575                if isinstance(field, exp.Column) and self._match(TokenType.DOT, advance=False):
5576                    field = self._parse_column_ops(field)
5577            else:
5578                field = self._parse_field(any_token=True, anonymous_func=True)
5579
5580            # Function calls can be qualified, e.g., x.y.FOO()
5581            # This converts the final AST to a series of Dots leading to the function call
5582            # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules
5583            if isinstance(field, (exp.Func, exp.Window)) and this:
5584                this = this.transform(
5585                    lambda n: n.to_dot(include_dots=False) if isinstance(n, exp.Column) else n
5586                )
5587
5588            if op:
5589                this = op(self, this, field)
5590            elif isinstance(this, exp.Column) and not this.args.get("catalog"):
5591                this = self.expression(
5592                    exp.Column,
5593                    comments=this.comments,
5594                    this=field,
5595                    table=this.this,
5596                    db=this.args.get("table"),
5597                    catalog=this.args.get("db"),
5598                )
5599            elif isinstance(field, exp.Window):
5600                # Move the exp.Dot's to the window's function
5601                window_func = self.expression(exp.Dot, this=this, expression=field.this)
5602                field.set("this", window_func)
5603                this = field
5604            else:
5605                this = self.expression(exp.Dot, this=this, expression=field)
5606
5607            if field and field.comments:
5608                t.cast(exp.Expression, this).add_comments(field.pop_comments())
5609
5610            this = self._parse_bracket(this)
5611
5612        return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
5613
5614    def _parse_paren(self) -> t.Optional[exp.Expression]:
5615        if not self._match(TokenType.L_PAREN):
5616            return None
5617
5618        comments = self._prev_comments
5619        query = self._parse_select()
5620
5621        if query:
5622            expressions = [query]
5623        else:
5624            expressions = self._parse_expressions()
5625
5626        this = self._parse_query_modifiers(seq_get(expressions, 0))
5627
5628        if not this and self._match(TokenType.R_PAREN, advance=False):
5629            this = self.expression(exp.Tuple)
5630        elif isinstance(this, exp.UNWRAPPED_QUERIES):
5631            this = self._parse_subquery(this=this, parse_alias=False)
5632        elif isinstance(this, exp.Subquery):
5633            this = self._parse_subquery(this=self._parse_set_operations(this), parse_alias=False)
5634        elif len(expressions) > 1 or self._prev.token_type == TokenType.COMMA:
5635            this = self.expression(exp.Tuple, expressions=expressions)
5636        else:
5637            this = self.expression(exp.Paren, this=this)
5638
5639        if this:
5640            this.add_comments(comments)
5641
5642        self._match_r_paren(expression=this)
5643        return this
5644
5645    def _parse_primary(self) -> t.Optional[exp.Expression]:
5646        if self._match_set(self.PRIMARY_PARSERS):
5647            token_type = self._prev.token_type
5648            primary = self.PRIMARY_PARSERS[token_type](self, self._prev)
5649
5650            if token_type == TokenType.STRING:
5651                expressions = [primary]
5652                while self._match(TokenType.STRING):
5653                    expressions.append(exp.Literal.string(self._prev.text))
5654
5655                if len(expressions) > 1:
5656                    return self.expression(exp.Concat, expressions=expressions)
5657
5658            return primary
5659
5660        if self._match_pair(TokenType.DOT, TokenType.NUMBER):
5661            return exp.Literal.number(f"0.{self._prev.text}")
5662
5663        return self._parse_paren()
5664
5665    def _parse_field(
5666        self,
5667        any_token: bool = False,
5668        tokens: t.Optional[t.Collection[TokenType]] = None,
5669        anonymous_func: bool = False,
5670    ) -> t.Optional[exp.Expression]:
5671        if anonymous_func:
5672            field = (
5673                self._parse_function(anonymous=anonymous_func, any_token=any_token)
5674                or self._parse_primary()
5675            )
5676        else:
5677            field = self._parse_primary() or self._parse_function(
5678                anonymous=anonymous_func, any_token=any_token
5679            )
5680        return field or self._parse_id_var(any_token=any_token, tokens=tokens)
5681
5682    def _parse_function(
5683        self,
5684        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5685        anonymous: bool = False,
5686        optional_parens: bool = True,
5687        any_token: bool = False,
5688    ) -> t.Optional[exp.Expression]:
5689        # This allows us to also parse {fn <function>} syntax (Snowflake, MySQL support this)
5690        # See: https://community.snowflake.com/s/article/SQL-Escape-Sequences
5691        fn_syntax = False
5692        if (
5693            self._match(TokenType.L_BRACE, advance=False)
5694            and self._next
5695            and self._next.text.upper() == "FN"
5696        ):
5697            self._advance(2)
5698            fn_syntax = True
5699
5700        func = self._parse_function_call(
5701            functions=functions,
5702            anonymous=anonymous,
5703            optional_parens=optional_parens,
5704            any_token=any_token,
5705        )
5706
5707        if fn_syntax:
5708            self._match(TokenType.R_BRACE)
5709
5710        return func
5711
5712    def _parse_function_call(
5713        self,
5714        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5715        anonymous: bool = False,
5716        optional_parens: bool = True,
5717        any_token: bool = False,
5718    ) -> t.Optional[exp.Expression]:
5719        if not self._curr:
5720            return None
5721
5722        comments = self._curr.comments
5723        token = self._curr
5724        token_type = self._curr.token_type
5725        this = self._curr.text
5726        upper = this.upper()
5727
5728        parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper)
5729        if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS:
5730            self._advance()
5731            return self._parse_window(parser(self))
5732
5733        if not self._next or self._next.token_type != TokenType.L_PAREN:
5734            if optional_parens and token_type in self.NO_PAREN_FUNCTIONS:
5735                self._advance()
5736                return self.expression(self.NO_PAREN_FUNCTIONS[token_type])
5737
5738            return None
5739
5740        if any_token:
5741            if token_type in self.RESERVED_TOKENS:
5742                return None
5743        elif token_type not in self.FUNC_TOKENS:
5744            return None
5745
5746        self._advance(2)
5747
5748        parser = self.FUNCTION_PARSERS.get(upper)
5749        if parser and not anonymous:
5750            this = parser(self)
5751        else:
5752            subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type)
5753
5754            if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH):
5755                this = self.expression(
5756                    subquery_predicate, comments=comments, this=self._parse_select()
5757                )
5758                self._match_r_paren()
5759                return this
5760
5761            if functions is None:
5762                functions = self.FUNCTIONS
5763
5764            function = functions.get(upper)
5765            known_function = function and not anonymous
5766
5767            alias = not known_function or upper in self.FUNCTIONS_WITH_ALIASED_ARGS
5768            args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
5769
5770            post_func_comments = self._curr and self._curr.comments
5771            if known_function and post_func_comments:
5772                # If the user-inputted comment "/* sqlglot.anonymous */" is following the function
5773                # call we'll construct it as exp.Anonymous, even if it's "known"
5774                if any(
5775                    comment.lstrip().startswith(exp.SQLGLOT_ANONYMOUS)
5776                    for comment in post_func_comments
5777                ):
5778                    known_function = False
5779
5780            if alias and known_function:
5781                args = self._kv_to_prop_eq(args)
5782
5783            if known_function:
5784                func_builder = t.cast(t.Callable, function)
5785
5786                if "dialect" in func_builder.__code__.co_varnames:
5787                    func = func_builder(args, dialect=self.dialect)
5788                else:
5789                    func = func_builder(args)
5790
5791                func = self.validate_expression(func, args)
5792                if self.dialect.PRESERVE_ORIGINAL_NAMES:
5793                    func.meta["name"] = this
5794
5795                this = func
5796            else:
5797                if token_type == TokenType.IDENTIFIER:
5798                    this = exp.Identifier(this=this, quoted=True).update_positions(token)
5799
5800                this = self.expression(exp.Anonymous, this=this, expressions=args)
5801                this = this.update_positions(token)
5802
5803        if isinstance(this, exp.Expression):
5804            this.add_comments(comments)
5805
5806        self._match_r_paren(this)
5807        return self._parse_window(this)
5808
5809    def _to_prop_eq(self, expression: exp.Expression, index: int) -> exp.Expression:
5810        return expression
5811
5812    def _kv_to_prop_eq(self, expressions: t.List[exp.Expression]) -> t.List[exp.Expression]:
5813        transformed = []
5814
5815        for index, e in enumerate(expressions):
5816            if isinstance(e, self.KEY_VALUE_DEFINITIONS):
5817                if isinstance(e, exp.Alias):
5818                    e = self.expression(exp.PropertyEQ, this=e.args.get("alias"), expression=e.this)
5819
5820                if not isinstance(e, exp.PropertyEQ):
5821                    e = self.expression(
5822                        exp.PropertyEQ, this=exp.to_identifier(e.this.name), expression=e.expression
5823                    )
5824
5825                if isinstance(e.this, exp.Column):
5826                    e.this.replace(e.this.this)
5827            else:
5828                e = self._to_prop_eq(e, index)
5829
5830            transformed.append(e)
5831
5832        return transformed
5833
5834    def _parse_user_defined_function_expression(self) -> t.Optional[exp.Expression]:
5835        return self._parse_statement()
5836
5837    def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
5838        return self._parse_column_def(this=self._parse_id_var(), computed_column=False)
5839
5840    def _parse_user_defined_function(
5841        self, kind: t.Optional[TokenType] = None
5842    ) -> t.Optional[exp.Expression]:
5843        this = self._parse_table_parts(schema=True)
5844
5845        if not self._match(TokenType.L_PAREN):
5846            return this
5847
5848        expressions = self._parse_csv(self._parse_function_parameter)
5849        self._match_r_paren()
5850        return self.expression(
5851            exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True
5852        )
5853
5854    def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier:
5855        literal = self._parse_primary()
5856        if literal:
5857            return self.expression(exp.Introducer, this=token.text, expression=literal)
5858
5859        return self._identifier_expression(token)
5860
5861    def _parse_session_parameter(self) -> exp.SessionParameter:
5862        kind = None
5863        this = self._parse_id_var() or self._parse_primary()
5864
5865        if this and self._match(TokenType.DOT):
5866            kind = this.name
5867            this = self._parse_var() or self._parse_primary()
5868
5869        return self.expression(exp.SessionParameter, this=this, kind=kind)
5870
5871    def _parse_lambda_arg(self) -> t.Optional[exp.Expression]:
5872        return self._parse_id_var()
5873
5874    def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]:
5875        index = self._index
5876
5877        if self._match(TokenType.L_PAREN):
5878            expressions = t.cast(
5879                t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_lambda_arg)
5880            )
5881
5882            if not self._match(TokenType.R_PAREN):
5883                self._retreat(index)
5884        else:
5885            expressions = [self._parse_lambda_arg()]
5886
5887        if self._match_set(self.LAMBDAS):
5888            return self.LAMBDAS[self._prev.token_type](self, expressions)
5889
5890        self._retreat(index)
5891
5892        this: t.Optional[exp.Expression]
5893
5894        if self._match(TokenType.DISTINCT):
5895            this = self.expression(
5896                exp.Distinct, expressions=self._parse_csv(self._parse_assignment)
5897            )
5898        else:
5899            this = self._parse_select_or_expression(alias=alias)
5900
5901        return self._parse_limit(
5902            self._parse_order(self._parse_having_max(self._parse_respect_or_ignore_nulls(this)))
5903        )
5904
5905    def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
5906        index = self._index
5907        if not self._match(TokenType.L_PAREN):
5908            return this
5909
5910        # Disambiguate between schema and subquery/CTE, e.g. in INSERT INTO table (<expr>),
5911        # expr can be of both types
5912        if self._match_set(self.SELECT_START_TOKENS):
5913            self._retreat(index)
5914            return this
5915        args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def())
5916        self._match_r_paren()
5917        return self.expression(exp.Schema, this=this, expressions=args)
5918
5919    def _parse_field_def(self) -> t.Optional[exp.Expression]:
5920        return self._parse_column_def(self._parse_field(any_token=True))
5921
5922    def _parse_column_def(
5923        self, this: t.Optional[exp.Expression], computed_column: bool = True
5924    ) -> t.Optional[exp.Expression]:
5925        # column defs are not really columns, they're identifiers
5926        if isinstance(this, exp.Column):
5927            this = this.this
5928
5929        if not computed_column:
5930            self._match(TokenType.ALIAS)
5931
5932        kind = self._parse_types(schema=True)
5933
5934        if self._match_text_seq("FOR", "ORDINALITY"):
5935            return self.expression(exp.ColumnDef, this=this, ordinality=True)
5936
5937        constraints: t.List[exp.Expression] = []
5938
5939        if (not kind and self._match(TokenType.ALIAS)) or self._match_texts(
5940            ("ALIAS", "MATERIALIZED")
5941        ):
5942            persisted = self._prev.text.upper() == "MATERIALIZED"
5943            constraint_kind = exp.ComputedColumnConstraint(
5944                this=self._parse_assignment(),
5945                persisted=persisted or self._match_text_seq("PERSISTED"),
5946                not_null=self._match_pair(TokenType.NOT, TokenType.NULL),
5947            )
5948            constraints.append(self.expression(exp.ColumnConstraint, kind=constraint_kind))
5949        elif (
5950            kind
5951            and self._match(TokenType.ALIAS, advance=False)
5952            and (
5953                not self.WRAPPED_TRANSFORM_COLUMN_CONSTRAINT
5954                or (self._next and self._next.token_type == TokenType.L_PAREN)
5955            )
5956        ):
5957            self._advance()
5958            constraints.append(
5959                self.expression(
5960                    exp.ColumnConstraint,
5961                    kind=exp.ComputedColumnConstraint(
5962                        this=self._parse_disjunction(),
5963                        persisted=self._match_texts(("STORED", "VIRTUAL"))
5964                        and self._prev.text.upper() == "STORED",
5965                    ),
5966                )
5967            )
5968
5969        while True:
5970            constraint = self._parse_column_constraint()
5971            if not constraint:
5972                break
5973            constraints.append(constraint)
5974
5975        if not kind and not constraints:
5976            return this
5977
5978        return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints)
5979
5980    def _parse_auto_increment(
5981        self,
5982    ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint:
5983        start = None
5984        increment = None
5985        order = None
5986
5987        if self._match(TokenType.L_PAREN, advance=False):
5988            args = self._parse_wrapped_csv(self._parse_bitwise)
5989            start = seq_get(args, 0)
5990            increment = seq_get(args, 1)
5991        elif self._match_text_seq("START"):
5992            start = self._parse_bitwise()
5993            self._match_text_seq("INCREMENT")
5994            increment = self._parse_bitwise()
5995            if self._match_text_seq("ORDER"):
5996                order = True
5997            elif self._match_text_seq("NOORDER"):
5998                order = False
5999
6000        if start and increment:
6001            return exp.GeneratedAsIdentityColumnConstraint(
6002                start=start, increment=increment, this=False, order=order
6003            )
6004
6005        return exp.AutoIncrementColumnConstraint()
6006
6007    def _parse_auto_property(self) -> t.Optional[exp.AutoRefreshProperty]:
6008        if not self._match_text_seq("REFRESH"):
6009            self._retreat(self._index - 1)
6010            return None
6011        return self.expression(exp.AutoRefreshProperty, this=self._parse_var(upper=True))
6012
6013    def _parse_compress(self) -> exp.CompressColumnConstraint:
6014        if self._match(TokenType.L_PAREN, advance=False):
6015            return self.expression(
6016                exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise)
6017            )
6018
6019        return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise())
6020
6021    def _parse_generated_as_identity(
6022        self,
6023    ) -> (
6024        exp.GeneratedAsIdentityColumnConstraint
6025        | exp.ComputedColumnConstraint
6026        | exp.GeneratedAsRowColumnConstraint
6027    ):
6028        if self._match_text_seq("BY", "DEFAULT"):
6029            on_null = self._match_pair(TokenType.ON, TokenType.NULL)
6030            this = self.expression(
6031                exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null
6032            )
6033        else:
6034            self._match_text_seq("ALWAYS")
6035            this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True)
6036
6037        self._match(TokenType.ALIAS)
6038
6039        if self._match_text_seq("ROW"):
6040            start = self._match_text_seq("START")
6041            if not start:
6042                self._match(TokenType.END)
6043            hidden = self._match_text_seq("HIDDEN")
6044            return self.expression(exp.GeneratedAsRowColumnConstraint, start=start, hidden=hidden)
6045
6046        identity = self._match_text_seq("IDENTITY")
6047
6048        if self._match(TokenType.L_PAREN):
6049            if self._match(TokenType.START_WITH):
6050                this.set("start", self._parse_bitwise())
6051            if self._match_text_seq("INCREMENT", "BY"):
6052                this.set("increment", self._parse_bitwise())
6053            if self._match_text_seq("MINVALUE"):
6054                this.set("minvalue", self._parse_bitwise())
6055            if self._match_text_seq("MAXVALUE"):
6056                this.set("maxvalue", self._parse_bitwise())
6057
6058            if self._match_text_seq("CYCLE"):
6059                this.set("cycle", True)
6060            elif self._match_text_seq("NO", "CYCLE"):
6061                this.set("cycle", False)
6062
6063            if not identity:
6064                this.set("expression", self._parse_range())
6065            elif not this.args.get("start") and self._match(TokenType.NUMBER, advance=False):
6066                args = self._parse_csv(self._parse_bitwise)
6067                this.set("start", seq_get(args, 0))
6068                this.set("increment", seq_get(args, 1))
6069
6070            self._match_r_paren()
6071
6072        return this
6073
6074    def _parse_inline(self) -> exp.InlineLengthColumnConstraint:
6075        self._match_text_seq("LENGTH")
6076        return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise())
6077
6078    def _parse_not_constraint(self) -> t.Optional[exp.Expression]:
6079        if self._match_text_seq("NULL"):
6080            return self.expression(exp.NotNullColumnConstraint)
6081        if self._match_text_seq("CASESPECIFIC"):
6082            return self.expression(exp.CaseSpecificColumnConstraint, not_=True)
6083        if self._match_text_seq("FOR", "REPLICATION"):
6084            return self.expression(exp.NotForReplicationColumnConstraint)
6085
6086        # Unconsume the `NOT` token
6087        self._retreat(self._index - 1)
6088        return None
6089
6090    def _parse_column_constraint(self) -> t.Optional[exp.Expression]:
6091        this = self._match(TokenType.CONSTRAINT) and self._parse_id_var()
6092
6093        procedure_option_follows = (
6094            self._match(TokenType.WITH, advance=False)
6095            and self._next
6096            and self._next.text.upper() in self.PROCEDURE_OPTIONS
6097        )
6098
6099        if not procedure_option_follows and self._match_texts(self.CONSTRAINT_PARSERS):
6100            return self.expression(
6101                exp.ColumnConstraint,
6102                this=this,
6103                kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self),
6104            )
6105
6106        return this
6107
6108    def _parse_constraint(self) -> t.Optional[exp.Expression]:
6109        if not self._match(TokenType.CONSTRAINT):
6110            return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS)
6111
6112        return self.expression(
6113            exp.Constraint,
6114            this=self._parse_id_var(),
6115            expressions=self._parse_unnamed_constraints(),
6116        )
6117
6118    def _parse_unnamed_constraints(self) -> t.List[exp.Expression]:
6119        constraints = []
6120        while True:
6121            constraint = self._parse_unnamed_constraint() or self._parse_function()
6122            if not constraint:
6123                break
6124            constraints.append(constraint)
6125
6126        return constraints
6127
6128    def _parse_unnamed_constraint(
6129        self, constraints: t.Optional[t.Collection[str]] = None
6130    ) -> t.Optional[exp.Expression]:
6131        if self._match(TokenType.IDENTIFIER, advance=False) or not self._match_texts(
6132            constraints or self.CONSTRAINT_PARSERS
6133        ):
6134            return None
6135
6136        constraint = self._prev.text.upper()
6137        if constraint not in self.CONSTRAINT_PARSERS:
6138            self.raise_error(f"No parser found for schema constraint {constraint}.")
6139
6140        return self.CONSTRAINT_PARSERS[constraint](self)
6141
6142    def _parse_unique_key(self) -> t.Optional[exp.Expression]:
6143        return self._parse_id_var(any_token=False)
6144
6145    def _parse_unique(self) -> exp.UniqueColumnConstraint:
6146        self._match_text_seq("KEY")
6147        return self.expression(
6148            exp.UniqueColumnConstraint,
6149            nulls=self._match_text_seq("NULLS", "NOT", "DISTINCT"),
6150            this=self._parse_schema(self._parse_unique_key()),
6151            index_type=self._match(TokenType.USING) and self._advance_any() and self._prev.text,
6152            on_conflict=self._parse_on_conflict(),
6153            options=self._parse_key_constraint_options(),
6154        )
6155
6156    def _parse_key_constraint_options(self) -> t.List[str]:
6157        options = []
6158        while True:
6159            if not self._curr:
6160                break
6161
6162            if self._match(TokenType.ON):
6163                action = None
6164                on = self._advance_any() and self._prev.text
6165
6166                if self._match_text_seq("NO", "ACTION"):
6167                    action = "NO ACTION"
6168                elif self._match_text_seq("CASCADE"):
6169                    action = "CASCADE"
6170                elif self._match_text_seq("RESTRICT"):
6171                    action = "RESTRICT"
6172                elif self._match_pair(TokenType.SET, TokenType.NULL):
6173                    action = "SET NULL"
6174                elif self._match_pair(TokenType.SET, TokenType.DEFAULT):
6175                    action = "SET DEFAULT"
6176                else:
6177                    self.raise_error("Invalid key constraint")
6178
6179                options.append(f"ON {on} {action}")
6180            else:
6181                var = self._parse_var_from_options(
6182                    self.KEY_CONSTRAINT_OPTIONS, raise_unmatched=False
6183                )
6184                if not var:
6185                    break
6186                options.append(var.name)
6187
6188        return options
6189
6190    def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]:
6191        if match and not self._match(TokenType.REFERENCES):
6192            return None
6193
6194        expressions = None
6195        this = self._parse_table(schema=True)
6196        options = self._parse_key_constraint_options()
6197        return self.expression(exp.Reference, this=this, expressions=expressions, options=options)
6198
6199    def _parse_foreign_key(self) -> exp.ForeignKey:
6200        expressions = (
6201            self._parse_wrapped_id_vars()
6202            if not self._match(TokenType.REFERENCES, advance=False)
6203            else None
6204        )
6205        reference = self._parse_references()
6206        on_options = {}
6207
6208        while self._match(TokenType.ON):
6209            if not self._match_set((TokenType.DELETE, TokenType.UPDATE)):
6210                self.raise_error("Expected DELETE or UPDATE")
6211
6212            kind = self._prev.text.lower()
6213
6214            if self._match_text_seq("NO", "ACTION"):
6215                action = "NO ACTION"
6216            elif self._match(TokenType.SET):
6217                self._match_set((TokenType.NULL, TokenType.DEFAULT))
6218                action = "SET " + self._prev.text.upper()
6219            else:
6220                self._advance()
6221                action = self._prev.text.upper()
6222
6223            on_options[kind] = action
6224
6225        return self.expression(
6226            exp.ForeignKey,
6227            expressions=expressions,
6228            reference=reference,
6229            options=self._parse_key_constraint_options(),
6230            **on_options,  # type: ignore
6231        )
6232
6233    def _parse_primary_key_part(self) -> t.Optional[exp.Expression]:
6234        return self._parse_ordered() or self._parse_field()
6235
6236    def _parse_period_for_system_time(self) -> t.Optional[exp.PeriodForSystemTimeConstraint]:
6237        if not self._match(TokenType.TIMESTAMP_SNAPSHOT):
6238            self._retreat(self._index - 1)
6239            return None
6240
6241        id_vars = self._parse_wrapped_id_vars()
6242        return self.expression(
6243            exp.PeriodForSystemTimeConstraint,
6244            this=seq_get(id_vars, 0),
6245            expression=seq_get(id_vars, 1),
6246        )
6247
6248    def _parse_primary_key(
6249        self, wrapped_optional: bool = False, in_props: bool = False
6250    ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey:
6251        desc = (
6252            self._match_set((TokenType.ASC, TokenType.DESC))
6253            and self._prev.token_type == TokenType.DESC
6254        )
6255
6256        if not in_props and not self._match(TokenType.L_PAREN, advance=False):
6257            return self.expression(
6258                exp.PrimaryKeyColumnConstraint,
6259                desc=desc,
6260                options=self._parse_key_constraint_options(),
6261            )
6262
6263        expressions = self._parse_wrapped_csv(
6264            self._parse_primary_key_part, optional=wrapped_optional
6265        )
6266        options = self._parse_key_constraint_options()
6267        return self.expression(exp.PrimaryKey, expressions=expressions, options=options)
6268
6269    def _parse_bracket_key_value(self, is_map: bool = False) -> t.Optional[exp.Expression]:
6270        return self._parse_slice(self._parse_alias(self._parse_assignment(), explicit=True))
6271
6272    def _parse_odbc_datetime_literal(self) -> exp.Expression:
6273        """
6274        Parses a datetime column in ODBC format. We parse the column into the corresponding
6275        types, for example `{d'yyyy-mm-dd'}` will be parsed as a `Date` column, exactly the
6276        same as we did for `DATE('yyyy-mm-dd')`.
6277
6278        Reference:
6279        https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals
6280        """
6281        self._match(TokenType.VAR)
6282        exp_class = self.ODBC_DATETIME_LITERALS[self._prev.text.lower()]
6283        expression = self.expression(exp_class=exp_class, this=self._parse_string())
6284        if not self._match(TokenType.R_BRACE):
6285            self.raise_error("Expected }")
6286        return expression
6287
6288    def _parse_bracket(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
6289        if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)):
6290            return this
6291
6292        bracket_kind = self._prev.token_type
6293        if (
6294            bracket_kind == TokenType.L_BRACE
6295            and self._curr
6296            and self._curr.token_type == TokenType.VAR
6297            and self._curr.text.lower() in self.ODBC_DATETIME_LITERALS
6298        ):
6299            return self._parse_odbc_datetime_literal()
6300
6301        expressions = self._parse_csv(
6302            lambda: self._parse_bracket_key_value(is_map=bracket_kind == TokenType.L_BRACE)
6303        )
6304
6305        if bracket_kind == TokenType.L_BRACKET and not self._match(TokenType.R_BRACKET):
6306            self.raise_error("Expected ]")
6307        elif bracket_kind == TokenType.L_BRACE and not self._match(TokenType.R_BRACE):
6308            self.raise_error("Expected }")
6309
6310        # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
6311        if bracket_kind == TokenType.L_BRACE:
6312            this = self.expression(exp.Struct, expressions=self._kv_to_prop_eq(expressions))
6313        elif not this:
6314            this = build_array_constructor(
6315                exp.Array, args=expressions, bracket_kind=bracket_kind, dialect=self.dialect
6316            )
6317        else:
6318            constructor_type = self.ARRAY_CONSTRUCTORS.get(this.name.upper())
6319            if constructor_type:
6320                return build_array_constructor(
6321                    constructor_type,
6322                    args=expressions,
6323                    bracket_kind=bracket_kind,
6324                    dialect=self.dialect,
6325                )
6326
6327            expressions = apply_index_offset(
6328                this, expressions, -self.dialect.INDEX_OFFSET, dialect=self.dialect
6329            )
6330            this = self.expression(
6331                exp.Bracket,
6332                this=this,
6333                expressions=expressions,
6334                comments=this.pop_comments(),
6335            )
6336
6337        self._add_comments(this)
6338        return self._parse_bracket(this)
6339
6340    def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6341        if self._match(TokenType.COLON):
6342            return self.expression(exp.Slice, this=this, expression=self._parse_assignment())
6343        return this
6344
6345    def _parse_case(self) -> t.Optional[exp.Expression]:
6346        ifs = []
6347        default = None
6348
6349        comments = self._prev_comments
6350        expression = self._parse_assignment()
6351
6352        while self._match(TokenType.WHEN):
6353            this = self._parse_assignment()
6354            self._match(TokenType.THEN)
6355            then = self._parse_assignment()
6356            ifs.append(self.expression(exp.If, this=this, true=then))
6357
6358        if self._match(TokenType.ELSE):
6359            default = self._parse_assignment()
6360
6361        if not self._match(TokenType.END):
6362            if isinstance(default, exp.Interval) and default.this.sql().upper() == "END":
6363                default = exp.column("interval")
6364            else:
6365                self.raise_error("Expected END after CASE", self._prev)
6366
6367        return self.expression(
6368            exp.Case, comments=comments, this=expression, ifs=ifs, default=default
6369        )
6370
6371    def _parse_if(self) -> t.Optional[exp.Expression]:
6372        if self._match(TokenType.L_PAREN):
6373            args = self._parse_csv(
6374                lambda: self._parse_alias(self._parse_assignment(), explicit=True)
6375            )
6376            this = self.validate_expression(exp.If.from_arg_list(args), args)
6377            self._match_r_paren()
6378        else:
6379            index = self._index - 1
6380
6381            if self.NO_PAREN_IF_COMMANDS and index == 0:
6382                return self._parse_as_command(self._prev)
6383
6384            condition = self._parse_assignment()
6385
6386            if not condition:
6387                self._retreat(index)
6388                return None
6389
6390            self._match(TokenType.THEN)
6391            true = self._parse_assignment()
6392            false = self._parse_assignment() if self._match(TokenType.ELSE) else None
6393            self._match(TokenType.END)
6394            this = self.expression(exp.If, this=condition, true=true, false=false)
6395
6396        return this
6397
6398    def _parse_next_value_for(self) -> t.Optional[exp.Expression]:
6399        if not self._match_text_seq("VALUE", "FOR"):
6400            self._retreat(self._index - 1)
6401            return None
6402
6403        return self.expression(
6404            exp.NextValueFor,
6405            this=self._parse_column(),
6406            order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order),
6407        )
6408
6409    def _parse_extract(self) -> exp.Extract | exp.Anonymous:
6410        this = self._parse_function() or self._parse_var_or_string(upper=True)
6411
6412        if self._match(TokenType.FROM):
6413            return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6414
6415        if not self._match(TokenType.COMMA):
6416            self.raise_error("Expected FROM or comma after EXTRACT", self._prev)
6417
6418        return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6419
6420    def _parse_gap_fill(self) -> exp.GapFill:
6421        self._match(TokenType.TABLE)
6422        this = self._parse_table()
6423
6424        self._match(TokenType.COMMA)
6425        args = [this, *self._parse_csv(self._parse_lambda)]
6426
6427        gap_fill = exp.GapFill.from_arg_list(args)
6428        return self.validate_expression(gap_fill, args)
6429
6430    def _parse_cast(self, strict: bool, safe: t.Optional[bool] = None) -> exp.Expression:
6431        this = self._parse_assignment()
6432
6433        if not self._match(TokenType.ALIAS):
6434            if self._match(TokenType.COMMA):
6435                return self.expression(exp.CastToStrType, this=this, to=self._parse_string())
6436
6437            self.raise_error("Expected AS after CAST")
6438
6439        fmt = None
6440        to = self._parse_types()
6441
6442        default = self._match(TokenType.DEFAULT)
6443        if default:
6444            default = self._parse_bitwise()
6445            self._match_text_seq("ON", "CONVERSION", "ERROR")
6446
6447        if self._match_set((TokenType.FORMAT, TokenType.COMMA)):
6448            fmt_string = self._parse_string()
6449            fmt = self._parse_at_time_zone(fmt_string)
6450
6451            if not to:
6452                to = exp.DataType.build(exp.DataType.Type.UNKNOWN)
6453            if to.this in exp.DataType.TEMPORAL_TYPES:
6454                this = self.expression(
6455                    exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime,
6456                    this=this,
6457                    format=exp.Literal.string(
6458                        format_time(
6459                            fmt_string.this if fmt_string else "",
6460                            self.dialect.FORMAT_MAPPING or self.dialect.TIME_MAPPING,
6461                            self.dialect.FORMAT_TRIE or self.dialect.TIME_TRIE,
6462                        )
6463                    ),
6464                    safe=safe,
6465                )
6466
6467                if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime):
6468                    this.set("zone", fmt.args["zone"])
6469                return this
6470        elif not to:
6471            self.raise_error("Expected TYPE after CAST")
6472        elif isinstance(to, exp.Identifier):
6473            to = exp.DataType.build(to.name, udt=True)
6474        elif to.this == exp.DataType.Type.CHAR:
6475            if self._match(TokenType.CHARACTER_SET):
6476                to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
6477
6478        return self.expression(
6479            exp.Cast if strict else exp.TryCast,
6480            this=this,
6481            to=to,
6482            format=fmt,
6483            safe=safe,
6484            action=self._parse_var_from_options(self.CAST_ACTIONS, raise_unmatched=False),
6485            default=default,
6486        )
6487
6488    def _parse_string_agg(self) -> exp.GroupConcat:
6489        if self._match(TokenType.DISTINCT):
6490            args: t.List[t.Optional[exp.Expression]] = [
6491                self.expression(exp.Distinct, expressions=[self._parse_assignment()])
6492            ]
6493            if self._match(TokenType.COMMA):
6494                args.extend(self._parse_csv(self._parse_assignment))
6495        else:
6496            args = self._parse_csv(self._parse_assignment)  # type: ignore
6497
6498        if self._match_text_seq("ON", "OVERFLOW"):
6499            # trino: LISTAGG(expression [, separator] [ON OVERFLOW overflow_behavior])
6500            if self._match_text_seq("ERROR"):
6501                on_overflow: t.Optional[exp.Expression] = exp.var("ERROR")
6502            else:
6503                self._match_text_seq("TRUNCATE")
6504                on_overflow = self.expression(
6505                    exp.OverflowTruncateBehavior,
6506                    this=self._parse_string(),
6507                    with_count=(
6508                        self._match_text_seq("WITH", "COUNT")
6509                        or not self._match_text_seq("WITHOUT", "COUNT")
6510                    ),
6511                )
6512        else:
6513            on_overflow = None
6514
6515        index = self._index
6516        if not self._match(TokenType.R_PAREN) and args:
6517            # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]])
6518            # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n])
6519            # The order is parsed through `this` as a canonicalization for WITHIN GROUPs
6520            args[0] = self._parse_limit(this=self._parse_order(this=args[0]))
6521            return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1))
6522
6523        # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]).
6524        # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that
6525        # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them.
6526        if not self._match_text_seq("WITHIN", "GROUP"):
6527            self._retreat(index)
6528            return self.validate_expression(exp.GroupConcat.from_arg_list(args), args)
6529
6530        # The corresponding match_r_paren will be called in parse_function (caller)
6531        self._match_l_paren()
6532
6533        return self.expression(
6534            exp.GroupConcat,
6535            this=self._parse_order(this=seq_get(args, 0)),
6536            separator=seq_get(args, 1),
6537            on_overflow=on_overflow,
6538        )
6539
6540    def _parse_convert(
6541        self, strict: bool, safe: t.Optional[bool] = None
6542    ) -> t.Optional[exp.Expression]:
6543        this = self._parse_bitwise()
6544
6545        if self._match(TokenType.USING):
6546            to: t.Optional[exp.Expression] = self.expression(
6547                exp.CharacterSet, this=self._parse_var()
6548            )
6549        elif self._match(TokenType.COMMA):
6550            to = self._parse_types()
6551        else:
6552            to = None
6553
6554        return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, safe=safe)
6555
6556    def _parse_xml_table(self) -> exp.XMLTable:
6557        namespaces = None
6558        passing = None
6559        columns = None
6560
6561        if self._match_text_seq("XMLNAMESPACES", "("):
6562            namespaces = self._parse_xml_namespace()
6563            self._match_text_seq(")", ",")
6564
6565        this = self._parse_string()
6566
6567        if self._match_text_seq("PASSING"):
6568            # The BY VALUE keywords are optional and are provided for semantic clarity
6569            self._match_text_seq("BY", "VALUE")
6570            passing = self._parse_csv(self._parse_column)
6571
6572        by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
6573
6574        if self._match_text_seq("COLUMNS"):
6575            columns = self._parse_csv(self._parse_field_def)
6576
6577        return self.expression(
6578            exp.XMLTable,
6579            this=this,
6580            namespaces=namespaces,
6581            passing=passing,
6582            columns=columns,
6583            by_ref=by_ref,
6584        )
6585
6586    def _parse_xml_namespace(self) -> t.List[exp.XMLNamespace]:
6587        namespaces = []
6588
6589        while True:
6590            if self._match(TokenType.DEFAULT):
6591                uri = self._parse_string()
6592            else:
6593                uri = self._parse_alias(self._parse_string())
6594            namespaces.append(self.expression(exp.XMLNamespace, this=uri))
6595            if not self._match(TokenType.COMMA):
6596                break
6597
6598        return namespaces
6599
6600    def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]:
6601        """
6602        There are generally two variants of the DECODE function:
6603
6604        - DECODE(bin, charset)
6605        - DECODE(expression, search, result [, search, result] ... [, default])
6606
6607        The second variant will always be parsed into a CASE expression. Note that NULL
6608        needs special treatment, since we need to explicitly check for it with `IS NULL`,
6609        instead of relying on pattern matching.
6610        """
6611        args = self._parse_csv(self._parse_assignment)
6612
6613        if len(args) < 3:
6614            return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1))
6615
6616        expression, *expressions = args
6617        if not expression:
6618            return None
6619
6620        ifs = []
6621        for search, result in zip(expressions[::2], expressions[1::2]):
6622            if not search or not result:
6623                return None
6624
6625            if isinstance(search, exp.Literal):
6626                ifs.append(
6627                    exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result)
6628                )
6629            elif isinstance(search, exp.Null):
6630                ifs.append(
6631                    exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result)
6632                )
6633            else:
6634                cond = exp.or_(
6635                    exp.EQ(this=expression.copy(), expression=search),
6636                    exp.and_(
6637                        exp.Is(this=expression.copy(), expression=exp.Null()),
6638                        exp.Is(this=search.copy(), expression=exp.Null()),
6639                        copy=False,
6640                    ),
6641                    copy=False,
6642                )
6643                ifs.append(exp.If(this=cond, true=result))
6644
6645        return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
6646
6647    def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]:
6648        self._match_text_seq("KEY")
6649        key = self._parse_column()
6650        self._match_set(self.JSON_KEY_VALUE_SEPARATOR_TOKENS)
6651        self._match_text_seq("VALUE")
6652        value = self._parse_bitwise()
6653
6654        if not key and not value:
6655            return None
6656        return self.expression(exp.JSONKeyValue, this=key, expression=value)
6657
6658    def _parse_format_json(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6659        if not this or not self._match_text_seq("FORMAT", "JSON"):
6660            return this
6661
6662        return self.expression(exp.FormatJson, this=this)
6663
6664    def _parse_on_condition(self) -> t.Optional[exp.OnCondition]:
6665        # MySQL uses "X ON EMPTY Y ON ERROR" (e.g. JSON_VALUE) while Oracle uses the opposite (e.g. JSON_EXISTS)
6666        if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR:
6667            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6668            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6669        else:
6670            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6671            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6672
6673        null = self._parse_on_handling("NULL", *self.ON_CONDITION_TOKENS)
6674
6675        if not empty and not error and not null:
6676            return None
6677
6678        return self.expression(
6679            exp.OnCondition,
6680            empty=empty,
6681            error=error,
6682            null=null,
6683        )
6684
6685    def _parse_on_handling(
6686        self, on: str, *values: str
6687    ) -> t.Optional[str] | t.Optional[exp.Expression]:
6688        # Parses the "X ON Y" or "DEFAULT <expr> ON Y syntax, e.g. NULL ON NULL (Oracle, T-SQL, MySQL)
6689        for value in values:
6690            if self._match_text_seq(value, "ON", on):
6691                return f"{value} ON {on}"
6692
6693        index = self._index
6694        if self._match(TokenType.DEFAULT):
6695            default_value = self._parse_bitwise()
6696            if self._match_text_seq("ON", on):
6697                return default_value
6698
6699            self._retreat(index)
6700
6701        return None
6702
6703    @t.overload
6704    def _parse_json_object(self, agg: Lit[False]) -> exp.JSONObject: ...
6705
6706    @t.overload
6707    def _parse_json_object(self, agg: Lit[True]) -> exp.JSONObjectAgg: ...
6708
6709    def _parse_json_object(self, agg=False):
6710        star = self._parse_star()
6711        expressions = (
6712            [star]
6713            if star
6714            else self._parse_csv(lambda: self._parse_format_json(self._parse_json_key_value()))
6715        )
6716        null_handling = self._parse_on_handling("NULL", "NULL", "ABSENT")
6717
6718        unique_keys = None
6719        if self._match_text_seq("WITH", "UNIQUE"):
6720            unique_keys = True
6721        elif self._match_text_seq("WITHOUT", "UNIQUE"):
6722            unique_keys = False
6723
6724        self._match_text_seq("KEYS")
6725
6726        return_type = self._match_text_seq("RETURNING") and self._parse_format_json(
6727            self._parse_type()
6728        )
6729        encoding = self._match_text_seq("ENCODING") and self._parse_var()
6730
6731        return self.expression(
6732            exp.JSONObjectAgg if agg else exp.JSONObject,
6733            expressions=expressions,
6734            null_handling=null_handling,
6735            unique_keys=unique_keys,
6736            return_type=return_type,
6737            encoding=encoding,
6738        )
6739
6740    # Note: this is currently incomplete; it only implements the "JSON_value_column" part
6741    def _parse_json_column_def(self) -> exp.JSONColumnDef:
6742        if not self._match_text_seq("NESTED"):
6743            this = self._parse_id_var()
6744            kind = self._parse_types(allow_identifiers=False)
6745            nested = None
6746        else:
6747            this = None
6748            kind = None
6749            nested = True
6750
6751        path = self._match_text_seq("PATH") and self._parse_string()
6752        nested_schema = nested and self._parse_json_schema()
6753
6754        return self.expression(
6755            exp.JSONColumnDef,
6756            this=this,
6757            kind=kind,
6758            path=path,
6759            nested_schema=nested_schema,
6760        )
6761
6762    def _parse_json_schema(self) -> exp.JSONSchema:
6763        self._match_text_seq("COLUMNS")
6764        return self.expression(
6765            exp.JSONSchema,
6766            expressions=self._parse_wrapped_csv(self._parse_json_column_def, optional=True),
6767        )
6768
6769    def _parse_json_table(self) -> exp.JSONTable:
6770        this = self._parse_format_json(self._parse_bitwise())
6771        path = self._match(TokenType.COMMA) and self._parse_string()
6772        error_handling = self._parse_on_handling("ERROR", "ERROR", "NULL")
6773        empty_handling = self._parse_on_handling("EMPTY", "ERROR", "NULL")
6774        schema = self._parse_json_schema()
6775
6776        return exp.JSONTable(
6777            this=this,
6778            schema=schema,
6779            path=path,
6780            error_handling=error_handling,
6781            empty_handling=empty_handling,
6782        )
6783
6784    def _parse_match_against(self) -> exp.MatchAgainst:
6785        expressions = self._parse_csv(self._parse_column)
6786
6787        self._match_text_seq(")", "AGAINST", "(")
6788
6789        this = self._parse_string()
6790
6791        if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"):
6792            modifier = "IN NATURAL LANGUAGE MODE"
6793            if self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6794                modifier = f"{modifier} WITH QUERY EXPANSION"
6795        elif self._match_text_seq("IN", "BOOLEAN", "MODE"):
6796            modifier = "IN BOOLEAN MODE"
6797        elif self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6798            modifier = "WITH QUERY EXPANSION"
6799        else:
6800            modifier = None
6801
6802        return self.expression(
6803            exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier
6804        )
6805
6806    # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16
6807    def _parse_open_json(self) -> exp.OpenJSON:
6808        this = self._parse_bitwise()
6809        path = self._match(TokenType.COMMA) and self._parse_string()
6810
6811        def _parse_open_json_column_def() -> exp.OpenJSONColumnDef:
6812            this = self._parse_field(any_token=True)
6813            kind = self._parse_types()
6814            path = self._parse_string()
6815            as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON)
6816
6817            return self.expression(
6818                exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json
6819            )
6820
6821        expressions = None
6822        if self._match_pair(TokenType.R_PAREN, TokenType.WITH):
6823            self._match_l_paren()
6824            expressions = self._parse_csv(_parse_open_json_column_def)
6825
6826        return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions)
6827
6828    def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition:
6829        args = self._parse_csv(self._parse_bitwise)
6830
6831        if self._match(TokenType.IN):
6832            return self.expression(
6833                exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0)
6834            )
6835
6836        if haystack_first:
6837            haystack = seq_get(args, 0)
6838            needle = seq_get(args, 1)
6839        else:
6840            haystack = seq_get(args, 1)
6841            needle = seq_get(args, 0)
6842
6843        return self.expression(
6844            exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2)
6845        )
6846
6847    def _parse_predict(self) -> exp.Predict:
6848        self._match_text_seq("MODEL")
6849        this = self._parse_table()
6850
6851        self._match(TokenType.COMMA)
6852        self._match_text_seq("TABLE")
6853
6854        return self.expression(
6855            exp.Predict,
6856            this=this,
6857            expression=self._parse_table(),
6858            params_struct=self._match(TokenType.COMMA) and self._parse_bitwise(),
6859        )
6860
6861    def _parse_join_hint(self, func_name: str) -> exp.JoinHint:
6862        args = self._parse_csv(self._parse_table)
6863        return exp.JoinHint(this=func_name.upper(), expressions=args)
6864
6865    def _parse_substring(self) -> exp.Substring:
6866        # Postgres supports the form: substring(string [from int] [for int])
6867        # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6
6868
6869        args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise))
6870
6871        if self._match(TokenType.FROM):
6872            args.append(self._parse_bitwise())
6873        if self._match(TokenType.FOR):
6874            if len(args) == 1:
6875                args.append(exp.Literal.number(1))
6876            args.append(self._parse_bitwise())
6877
6878        return self.validate_expression(exp.Substring.from_arg_list(args), args)
6879
6880    def _parse_trim(self) -> exp.Trim:
6881        # https://www.w3resource.com/sql/character-functions/trim.php
6882        # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html
6883
6884        position = None
6885        collation = None
6886        expression = None
6887
6888        if self._match_texts(self.TRIM_TYPES):
6889            position = self._prev.text.upper()
6890
6891        this = self._parse_bitwise()
6892        if self._match_set((TokenType.FROM, TokenType.COMMA)):
6893            invert_order = self._prev.token_type == TokenType.FROM or self.TRIM_PATTERN_FIRST
6894            expression = self._parse_bitwise()
6895
6896            if invert_order:
6897                this, expression = expression, this
6898
6899        if self._match(TokenType.COLLATE):
6900            collation = self._parse_bitwise()
6901
6902        return self.expression(
6903            exp.Trim, this=this, position=position, expression=expression, collation=collation
6904        )
6905
6906    def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]:
6907        return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window)
6908
6909    def _parse_named_window(self) -> t.Optional[exp.Expression]:
6910        return self._parse_window(self._parse_id_var(), alias=True)
6911
6912    def _parse_respect_or_ignore_nulls(
6913        self, this: t.Optional[exp.Expression]
6914    ) -> t.Optional[exp.Expression]:
6915        if self._match_text_seq("IGNORE", "NULLS"):
6916            return self.expression(exp.IgnoreNulls, this=this)
6917        if self._match_text_seq("RESPECT", "NULLS"):
6918            return self.expression(exp.RespectNulls, this=this)
6919        return this
6920
6921    def _parse_having_max(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6922        if self._match(TokenType.HAVING):
6923            self._match_texts(("MAX", "MIN"))
6924            max = self._prev.text.upper() != "MIN"
6925            return self.expression(
6926                exp.HavingMax, this=this, expression=self._parse_column(), max=max
6927            )
6928
6929        return this
6930
6931    def _parse_window(
6932        self, this: t.Optional[exp.Expression], alias: bool = False
6933    ) -> t.Optional[exp.Expression]:
6934        func = this
6935        comments = func.comments if isinstance(func, exp.Expression) else None
6936
6937        # T-SQL allows the OVER (...) syntax after WITHIN GROUP.
6938        # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16
6939        if self._match_text_seq("WITHIN", "GROUP"):
6940            order = self._parse_wrapped(self._parse_order)
6941            this = self.expression(exp.WithinGroup, this=this, expression=order)
6942
6943        if self._match_pair(TokenType.FILTER, TokenType.L_PAREN):
6944            self._match(TokenType.WHERE)
6945            this = self.expression(
6946                exp.Filter, this=this, expression=self._parse_where(skip_where_token=True)
6947            )
6948            self._match_r_paren()
6949
6950        # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER
6951        # Some dialects choose to implement and some do not.
6952        # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
6953
6954        # There is some code above in _parse_lambda that handles
6955        #   SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ...
6956
6957        # The below changes handle
6958        #   SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ...
6959
6960        # Oracle allows both formats
6961        #   (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html)
6962        #   and Snowflake chose to do the same for familiarity
6963        #   https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes
6964        if isinstance(this, exp.AggFunc):
6965            ignore_respect = this.find(exp.IgnoreNulls, exp.RespectNulls)
6966
6967            if ignore_respect and ignore_respect is not this:
6968                ignore_respect.replace(ignore_respect.this)
6969                this = self.expression(ignore_respect.__class__, this=this)
6970
6971        this = self._parse_respect_or_ignore_nulls(this)
6972
6973        # bigquery select from window x AS (partition by ...)
6974        if alias:
6975            over = None
6976            self._match(TokenType.ALIAS)
6977        elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS):
6978            return this
6979        else:
6980            over = self._prev.text.upper()
6981
6982        if comments and isinstance(func, exp.Expression):
6983            func.pop_comments()
6984
6985        if not self._match(TokenType.L_PAREN):
6986            return self.expression(
6987                exp.Window,
6988                comments=comments,
6989                this=this,
6990                alias=self._parse_id_var(False),
6991                over=over,
6992            )
6993
6994        window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS)
6995
6996        first = self._match(TokenType.FIRST)
6997        if self._match_text_seq("LAST"):
6998            first = False
6999
7000        partition, order = self._parse_partition_and_order()
7001        kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text
7002
7003        if kind:
7004            self._match(TokenType.BETWEEN)
7005            start = self._parse_window_spec()
7006            self._match(TokenType.AND)
7007            end = self._parse_window_spec()
7008            exclude = (
7009                self._parse_var_from_options(self.WINDOW_EXCLUDE_OPTIONS)
7010                if self._match_text_seq("EXCLUDE")
7011                else None
7012            )
7013
7014            spec = self.expression(
7015                exp.WindowSpec,
7016                kind=kind,
7017                start=start["value"],
7018                start_side=start["side"],
7019                end=end["value"],
7020                end_side=end["side"],
7021                exclude=exclude,
7022            )
7023        else:
7024            spec = None
7025
7026        self._match_r_paren()
7027
7028        window = self.expression(
7029            exp.Window,
7030            comments=comments,
7031            this=this,
7032            partition_by=partition,
7033            order=order,
7034            spec=spec,
7035            alias=window_alias,
7036            over=over,
7037            first=first,
7038        )
7039
7040        # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...)
7041        if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False):
7042            return self._parse_window(window, alias=alias)
7043
7044        return window
7045
7046    def _parse_partition_and_order(
7047        self,
7048    ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]:
7049        return self._parse_partition_by(), self._parse_order()
7050
7051    def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]:
7052        self._match(TokenType.BETWEEN)
7053
7054        return {
7055            "value": (
7056                (self._match_text_seq("UNBOUNDED") and "UNBOUNDED")
7057                or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW")
7058                or self._parse_bitwise()
7059            ),
7060            "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text,
7061        }
7062
7063    def _parse_alias(
7064        self, this: t.Optional[exp.Expression], explicit: bool = False
7065    ) -> t.Optional[exp.Expression]:
7066        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
7067        # so this section tries to parse the clause version and if it fails, it treats the token
7068        # as an identifier (alias)
7069        if self._can_parse_limit_or_offset():
7070            return this
7071
7072        any_token = self._match(TokenType.ALIAS)
7073        comments = self._prev_comments or []
7074
7075        if explicit and not any_token:
7076            return this
7077
7078        if self._match(TokenType.L_PAREN):
7079            aliases = self.expression(
7080                exp.Aliases,
7081                comments=comments,
7082                this=this,
7083                expressions=self._parse_csv(lambda: self._parse_id_var(any_token)),
7084            )
7085            self._match_r_paren(aliases)
7086            return aliases
7087
7088        alias = self._parse_id_var(any_token, tokens=self.ALIAS_TOKENS) or (
7089            self.STRING_ALIASES and self._parse_string_as_identifier()
7090        )
7091
7092        if alias:
7093            comments.extend(alias.pop_comments())
7094            this = self.expression(exp.Alias, comments=comments, this=this, alias=alias)
7095            column = this.this
7096
7097            # Moves the comment next to the alias in `expr /* comment */ AS alias`
7098            if not this.comments and column and column.comments:
7099                this.comments = column.pop_comments()
7100
7101        return this
7102
7103    def _parse_id_var(
7104        self,
7105        any_token: bool = True,
7106        tokens: t.Optional[t.Collection[TokenType]] = None,
7107    ) -> t.Optional[exp.Expression]:
7108        expression = self._parse_identifier()
7109        if not expression and (
7110            (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS)
7111        ):
7112            quoted = self._prev.token_type == TokenType.STRING
7113            expression = self._identifier_expression(quoted=quoted)
7114
7115        return expression
7116
7117    def _parse_string(self) -> t.Optional[exp.Expression]:
7118        if self._match_set(self.STRING_PARSERS):
7119            return self.STRING_PARSERS[self._prev.token_type](self, self._prev)
7120        return self._parse_placeholder()
7121
7122    def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]:
7123        output = exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True)
7124        if output:
7125            output.update_positions(self._prev)
7126        return output
7127
7128    def _parse_number(self) -> t.Optional[exp.Expression]:
7129        if self._match_set(self.NUMERIC_PARSERS):
7130            return self.NUMERIC_PARSERS[self._prev.token_type](self, self._prev)
7131        return self._parse_placeholder()
7132
7133    def _parse_identifier(self) -> t.Optional[exp.Expression]:
7134        if self._match(TokenType.IDENTIFIER):
7135            return self._identifier_expression(quoted=True)
7136        return self._parse_placeholder()
7137
7138    def _parse_var(
7139        self,
7140        any_token: bool = False,
7141        tokens: t.Optional[t.Collection[TokenType]] = None,
7142        upper: bool = False,
7143    ) -> t.Optional[exp.Expression]:
7144        if (
7145            (any_token and self._advance_any())
7146            or self._match(TokenType.VAR)
7147            or (self._match_set(tokens) if tokens else False)
7148        ):
7149            return self.expression(
7150                exp.Var, this=self._prev.text.upper() if upper else self._prev.text
7151            )
7152        return self._parse_placeholder()
7153
7154    def _advance_any(self, ignore_reserved: bool = False) -> t.Optional[Token]:
7155        if self._curr and (ignore_reserved or self._curr.token_type not in self.RESERVED_TOKENS):
7156            self._advance()
7157            return self._prev
7158        return None
7159
7160    def _parse_var_or_string(self, upper: bool = False) -> t.Optional[exp.Expression]:
7161        return self._parse_string() or self._parse_var(any_token=True, upper=upper)
7162
7163    def _parse_primary_or_var(self) -> t.Optional[exp.Expression]:
7164        return self._parse_primary() or self._parse_var(any_token=True)
7165
7166    def _parse_null(self) -> t.Optional[exp.Expression]:
7167        if self._match_set(self.NULL_TOKENS):
7168            return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev)
7169        return self._parse_placeholder()
7170
7171    def _parse_boolean(self) -> t.Optional[exp.Expression]:
7172        if self._match(TokenType.TRUE):
7173            return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev)
7174        if self._match(TokenType.FALSE):
7175            return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev)
7176        return self._parse_placeholder()
7177
7178    def _parse_star(self) -> t.Optional[exp.Expression]:
7179        if self._match(TokenType.STAR):
7180            return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev)
7181        return self._parse_placeholder()
7182
7183    def _parse_parameter(self) -> exp.Parameter:
7184        this = self._parse_identifier() or self._parse_primary_or_var()
7185        return self.expression(exp.Parameter, this=this)
7186
7187    def _parse_placeholder(self) -> t.Optional[exp.Expression]:
7188        if self._match_set(self.PLACEHOLDER_PARSERS):
7189            placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self)
7190            if placeholder:
7191                return placeholder
7192            self._advance(-1)
7193        return None
7194
7195    def _parse_star_op(self, *keywords: str) -> t.Optional[t.List[exp.Expression]]:
7196        if not self._match_texts(keywords):
7197            return None
7198        if self._match(TokenType.L_PAREN, advance=False):
7199            return self._parse_wrapped_csv(self._parse_expression)
7200
7201        expression = self._parse_expression()
7202        return [expression] if expression else None
7203
7204    def _parse_csv(
7205        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA
7206    ) -> t.List[exp.Expression]:
7207        parse_result = parse_method()
7208        items = [parse_result] if parse_result is not None else []
7209
7210        while self._match(sep):
7211            self._add_comments(parse_result)
7212            parse_result = parse_method()
7213            if parse_result is not None:
7214                items.append(parse_result)
7215
7216        return items
7217
7218    def _parse_tokens(
7219        self, parse_method: t.Callable, expressions: t.Dict
7220    ) -> t.Optional[exp.Expression]:
7221        this = parse_method()
7222
7223        while self._match_set(expressions):
7224            this = self.expression(
7225                expressions[self._prev.token_type],
7226                this=this,
7227                comments=self._prev_comments,
7228                expression=parse_method(),
7229            )
7230
7231        return this
7232
7233    def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]:
7234        return self._parse_wrapped_csv(self._parse_id_var, optional=optional)
7235
7236    def _parse_wrapped_csv(
7237        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False
7238    ) -> t.List[exp.Expression]:
7239        return self._parse_wrapped(
7240            lambda: self._parse_csv(parse_method, sep=sep), optional=optional
7241        )
7242
7243    def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any:
7244        wrapped = self._match(TokenType.L_PAREN)
7245        if not wrapped and not optional:
7246            self.raise_error("Expecting (")
7247        parse_result = parse_method()
7248        if wrapped:
7249            self._match_r_paren()
7250        return parse_result
7251
7252    def _parse_expressions(self) -> t.List[exp.Expression]:
7253        return self._parse_csv(self._parse_expression)
7254
7255    def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]:
7256        return self._parse_select() or self._parse_set_operations(
7257            self._parse_alias(self._parse_assignment(), explicit=True)
7258            if alias
7259            else self._parse_assignment()
7260        )
7261
7262    def _parse_ddl_select(self) -> t.Optional[exp.Expression]:
7263        return self._parse_query_modifiers(
7264            self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False))
7265        )
7266
7267    def _parse_transaction(self) -> exp.Transaction | exp.Command:
7268        this = None
7269        if self._match_texts(self.TRANSACTION_KIND):
7270            this = self._prev.text
7271
7272        self._match_texts(("TRANSACTION", "WORK"))
7273
7274        modes = []
7275        while True:
7276            mode = []
7277            while self._match(TokenType.VAR):
7278                mode.append(self._prev.text)
7279
7280            if mode:
7281                modes.append(" ".join(mode))
7282            if not self._match(TokenType.COMMA):
7283                break
7284
7285        return self.expression(exp.Transaction, this=this, modes=modes)
7286
7287    def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback:
7288        chain = None
7289        savepoint = None
7290        is_rollback = self._prev.token_type == TokenType.ROLLBACK
7291
7292        self._match_texts(("TRANSACTION", "WORK"))
7293
7294        if self._match_text_seq("TO"):
7295            self._match_text_seq("SAVEPOINT")
7296            savepoint = self._parse_id_var()
7297
7298        if self._match(TokenType.AND):
7299            chain = not self._match_text_seq("NO")
7300            self._match_text_seq("CHAIN")
7301
7302        if is_rollback:
7303            return self.expression(exp.Rollback, savepoint=savepoint)
7304
7305        return self.expression(exp.Commit, chain=chain)
7306
7307    def _parse_refresh(self) -> exp.Refresh:
7308        self._match(TokenType.TABLE)
7309        return self.expression(exp.Refresh, this=self._parse_string() or self._parse_table())
7310
7311    def _parse_add_column(self) -> t.Optional[exp.ColumnDef]:
7312        if not self._prev.text.upper() == "ADD":
7313            return None
7314
7315        start = self._index
7316        self._match(TokenType.COLUMN)
7317
7318        exists_column = self._parse_exists(not_=True)
7319        expression = self._parse_field_def()
7320
7321        if not isinstance(expression, exp.ColumnDef):
7322            self._retreat(start)
7323            return None
7324
7325        expression.set("exists", exists_column)
7326
7327        # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns
7328        if self._match_texts(("FIRST", "AFTER")):
7329            position = self._prev.text
7330            column_position = self.expression(
7331                exp.ColumnPosition, this=self._parse_column(), position=position
7332            )
7333            expression.set("position", column_position)
7334
7335        return expression
7336
7337    def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]:
7338        drop = self._match(TokenType.DROP) and self._parse_drop()
7339        if drop and not isinstance(drop, exp.Command):
7340            drop.set("kind", drop.args.get("kind", "COLUMN"))
7341        return drop
7342
7343    # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html
7344    def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition:
7345        return self.expression(
7346            exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists
7347        )
7348
7349    def _parse_alter_table_add(self) -> t.List[exp.Expression]:
7350        def _parse_add_alteration() -> t.Optional[exp.Expression]:
7351            self._match_text_seq("ADD")
7352            if self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False):
7353                return self.expression(
7354                    exp.AddConstraint, expressions=self._parse_csv(self._parse_constraint)
7355                )
7356
7357            column_def = self._parse_add_column()
7358            if isinstance(column_def, exp.ColumnDef):
7359                return column_def
7360
7361            exists = self._parse_exists(not_=True)
7362            if self._match_pair(TokenType.PARTITION, TokenType.L_PAREN, advance=False):
7363                return self.expression(
7364                    exp.AddPartition, exists=exists, this=self._parse_field(any_token=True)
7365                )
7366
7367            return None
7368
7369        if not self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False) and (
7370            not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
7371            or self._match_text_seq("COLUMNS")
7372        ):
7373            schema = self._parse_schema()
7374
7375            return ensure_list(schema) if schema else self._parse_csv(self._parse_field_def)
7376
7377        return self._parse_csv(_parse_add_alteration)
7378
7379    def _parse_alter_table_alter(self) -> t.Optional[exp.Expression]:
7380        if self._match_texts(self.ALTER_ALTER_PARSERS):
7381            return self.ALTER_ALTER_PARSERS[self._prev.text.upper()](self)
7382
7383        # Many dialects support the ALTER [COLUMN] syntax, so if there is no
7384        # keyword after ALTER we default to parsing this statement
7385        self._match(TokenType.COLUMN)
7386        column = self._parse_field(any_token=True)
7387
7388        if self._match_pair(TokenType.DROP, TokenType.DEFAULT):
7389            return self.expression(exp.AlterColumn, this=column, drop=True)
7390        if self._match_pair(TokenType.SET, TokenType.DEFAULT):
7391            return self.expression(exp.AlterColumn, this=column, default=self._parse_assignment())
7392        if self._match(TokenType.COMMENT):
7393            return self.expression(exp.AlterColumn, this=column, comment=self._parse_string())
7394        if self._match_text_seq("DROP", "NOT", "NULL"):
7395            return self.expression(
7396                exp.AlterColumn,
7397                this=column,
7398                drop=True,
7399                allow_null=True,
7400            )
7401        if self._match_text_seq("SET", "NOT", "NULL"):
7402            return self.expression(
7403                exp.AlterColumn,
7404                this=column,
7405                allow_null=False,
7406            )
7407
7408        if self._match_text_seq("SET", "VISIBLE"):
7409            return self.expression(exp.AlterColumn, this=column, visible="VISIBLE")
7410        if self._match_text_seq("SET", "INVISIBLE"):
7411            return self.expression(exp.AlterColumn, this=column, visible="INVISIBLE")
7412
7413        self._match_text_seq("SET", "DATA")
7414        self._match_text_seq("TYPE")
7415        return self.expression(
7416            exp.AlterColumn,
7417            this=column,
7418            dtype=self._parse_types(),
7419            collate=self._match(TokenType.COLLATE) and self._parse_term(),
7420            using=self._match(TokenType.USING) and self._parse_assignment(),
7421        )
7422
7423    def _parse_alter_diststyle(self) -> exp.AlterDistStyle:
7424        if self._match_texts(("ALL", "EVEN", "AUTO")):
7425            return self.expression(exp.AlterDistStyle, this=exp.var(self._prev.text.upper()))
7426
7427        self._match_text_seq("KEY", "DISTKEY")
7428        return self.expression(exp.AlterDistStyle, this=self._parse_column())
7429
7430    def _parse_alter_sortkey(self, compound: t.Optional[bool] = None) -> exp.AlterSortKey:
7431        if compound:
7432            self._match_text_seq("SORTKEY")
7433
7434        if self._match(TokenType.L_PAREN, advance=False):
7435            return self.expression(
7436                exp.AlterSortKey, expressions=self._parse_wrapped_id_vars(), compound=compound
7437            )
7438
7439        self._match_texts(("AUTO", "NONE"))
7440        return self.expression(
7441            exp.AlterSortKey, this=exp.var(self._prev.text.upper()), compound=compound
7442        )
7443
7444    def _parse_alter_table_drop(self) -> t.List[exp.Expression]:
7445        index = self._index - 1
7446
7447        partition_exists = self._parse_exists()
7448        if self._match(TokenType.PARTITION, advance=False):
7449            return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists))
7450
7451        self._retreat(index)
7452        return self._parse_csv(self._parse_drop_column)
7453
7454    def _parse_alter_table_rename(self) -> t.Optional[exp.AlterRename | exp.RenameColumn]:
7455        if self._match(TokenType.COLUMN) or not self.ALTER_RENAME_REQUIRES_COLUMN:
7456            exists = self._parse_exists()
7457            old_column = self._parse_column()
7458            to = self._match_text_seq("TO")
7459            new_column = self._parse_column()
7460
7461            if old_column is None or to is None or new_column is None:
7462                return None
7463
7464            return self.expression(exp.RenameColumn, this=old_column, to=new_column, exists=exists)
7465
7466        self._match_text_seq("TO")
7467        return self.expression(exp.AlterRename, this=self._parse_table(schema=True))
7468
7469    def _parse_alter_table_set(self) -> exp.AlterSet:
7470        alter_set = self.expression(exp.AlterSet)
7471
7472        if self._match(TokenType.L_PAREN, advance=False) or self._match_text_seq(
7473            "TABLE", "PROPERTIES"
7474        ):
7475            alter_set.set("expressions", self._parse_wrapped_csv(self._parse_assignment))
7476        elif self._match_text_seq("FILESTREAM_ON", advance=False):
7477            alter_set.set("expressions", [self._parse_assignment()])
7478        elif self._match_texts(("LOGGED", "UNLOGGED")):
7479            alter_set.set("option", exp.var(self._prev.text.upper()))
7480        elif self._match_text_seq("WITHOUT") and self._match_texts(("CLUSTER", "OIDS")):
7481            alter_set.set("option", exp.var(f"WITHOUT {self._prev.text.upper()}"))
7482        elif self._match_text_seq("LOCATION"):
7483            alter_set.set("location", self._parse_field())
7484        elif self._match_text_seq("ACCESS", "METHOD"):
7485            alter_set.set("access_method", self._parse_field())
7486        elif self._match_text_seq("TABLESPACE"):
7487            alter_set.set("tablespace", self._parse_field())
7488        elif self._match_text_seq("FILE", "FORMAT") or self._match_text_seq("FILEFORMAT"):
7489            alter_set.set("file_format", [self._parse_field()])
7490        elif self._match_text_seq("STAGE_FILE_FORMAT"):
7491            alter_set.set("file_format", self._parse_wrapped_options())
7492        elif self._match_text_seq("STAGE_COPY_OPTIONS"):
7493            alter_set.set("copy_options", self._parse_wrapped_options())
7494        elif self._match_text_seq("TAG") or self._match_text_seq("TAGS"):
7495            alter_set.set("tag", self._parse_csv(self._parse_assignment))
7496        else:
7497            if self._match_text_seq("SERDE"):
7498                alter_set.set("serde", self._parse_field())
7499
7500            properties = self._parse_wrapped(self._parse_properties, optional=True)
7501            alter_set.set("expressions", [properties])
7502
7503        return alter_set
7504
7505    def _parse_alter(self) -> exp.Alter | exp.Command:
7506        start = self._prev
7507
7508        alter_token = self._match_set(self.ALTERABLES) and self._prev
7509        if not alter_token:
7510            return self._parse_as_command(start)
7511
7512        exists = self._parse_exists()
7513        only = self._match_text_seq("ONLY")
7514        this = self._parse_table(schema=True)
7515        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
7516
7517        if self._next:
7518            self._advance()
7519
7520        parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None
7521        if parser:
7522            actions = ensure_list(parser(self))
7523            not_valid = self._match_text_seq("NOT", "VALID")
7524            options = self._parse_csv(self._parse_property)
7525
7526            if not self._curr and actions:
7527                return self.expression(
7528                    exp.Alter,
7529                    this=this,
7530                    kind=alter_token.text.upper(),
7531                    exists=exists,
7532                    actions=actions,
7533                    only=only,
7534                    options=options,
7535                    cluster=cluster,
7536                    not_valid=not_valid,
7537                )
7538
7539        return self._parse_as_command(start)
7540
7541    def _parse_analyze(self) -> exp.Analyze | exp.Command:
7542        start = self._prev
7543        # https://duckdb.org/docs/sql/statements/analyze
7544        if not self._curr:
7545            return self.expression(exp.Analyze)
7546
7547        options = []
7548        while self._match_texts(self.ANALYZE_STYLES):
7549            if self._prev.text.upper() == "BUFFER_USAGE_LIMIT":
7550                options.append(f"BUFFER_USAGE_LIMIT {self._parse_number()}")
7551            else:
7552                options.append(self._prev.text.upper())
7553
7554        this: t.Optional[exp.Expression] = None
7555        inner_expression: t.Optional[exp.Expression] = None
7556
7557        kind = self._curr and self._curr.text.upper()
7558
7559        if self._match(TokenType.TABLE) or self._match(TokenType.INDEX):
7560            this = self._parse_table_parts()
7561        elif self._match_text_seq("TABLES"):
7562            if self._match_set((TokenType.FROM, TokenType.IN)):
7563                kind = f"{kind} {self._prev.text.upper()}"
7564                this = self._parse_table(schema=True, is_db_reference=True)
7565        elif self._match_text_seq("DATABASE"):
7566            this = self._parse_table(schema=True, is_db_reference=True)
7567        elif self._match_text_seq("CLUSTER"):
7568            this = self._parse_table()
7569        # Try matching inner expr keywords before fallback to parse table.
7570        elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7571            kind = None
7572            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7573        else:
7574            # Empty kind  https://prestodb.io/docs/current/sql/analyze.html
7575            kind = None
7576            this = self._parse_table_parts()
7577
7578        partition = self._try_parse(self._parse_partition)
7579        if not partition and self._match_texts(self.PARTITION_KEYWORDS):
7580            return self._parse_as_command(start)
7581
7582        # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7583        if self._match_text_seq("WITH", "SYNC", "MODE") or self._match_text_seq(
7584            "WITH", "ASYNC", "MODE"
7585        ):
7586            mode = f"WITH {self._tokens[self._index - 2].text.upper()} MODE"
7587        else:
7588            mode = None
7589
7590        if self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7591            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7592
7593        properties = self._parse_properties()
7594        return self.expression(
7595            exp.Analyze,
7596            kind=kind,
7597            this=this,
7598            mode=mode,
7599            partition=partition,
7600            properties=properties,
7601            expression=inner_expression,
7602            options=options,
7603        )
7604
7605    # https://spark.apache.org/docs/3.5.1/sql-ref-syntax-aux-analyze-table.html
7606    def _parse_analyze_statistics(self) -> exp.AnalyzeStatistics:
7607        this = None
7608        kind = self._prev.text.upper()
7609        option = self._prev.text.upper() if self._match_text_seq("DELTA") else None
7610        expressions = []
7611
7612        if not self._match_text_seq("STATISTICS"):
7613            self.raise_error("Expecting token STATISTICS")
7614
7615        if self._match_text_seq("NOSCAN"):
7616            this = "NOSCAN"
7617        elif self._match(TokenType.FOR):
7618            if self._match_text_seq("ALL", "COLUMNS"):
7619                this = "FOR ALL COLUMNS"
7620            if self._match_texts("COLUMNS"):
7621                this = "FOR COLUMNS"
7622                expressions = self._parse_csv(self._parse_column_reference)
7623        elif self._match_text_seq("SAMPLE"):
7624            sample = self._parse_number()
7625            expressions = [
7626                self.expression(
7627                    exp.AnalyzeSample,
7628                    sample=sample,
7629                    kind=self._prev.text.upper() if self._match(TokenType.PERCENT) else None,
7630                )
7631            ]
7632
7633        return self.expression(
7634            exp.AnalyzeStatistics, kind=kind, option=option, this=this, expressions=expressions
7635        )
7636
7637    # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ANALYZE.html
7638    def _parse_analyze_validate(self) -> exp.AnalyzeValidate:
7639        kind = None
7640        this = None
7641        expression: t.Optional[exp.Expression] = None
7642        if self._match_text_seq("REF", "UPDATE"):
7643            kind = "REF"
7644            this = "UPDATE"
7645            if self._match_text_seq("SET", "DANGLING", "TO", "NULL"):
7646                this = "UPDATE SET DANGLING TO NULL"
7647        elif self._match_text_seq("STRUCTURE"):
7648            kind = "STRUCTURE"
7649            if self._match_text_seq("CASCADE", "FAST"):
7650                this = "CASCADE FAST"
7651            elif self._match_text_seq("CASCADE", "COMPLETE") and self._match_texts(
7652                ("ONLINE", "OFFLINE")
7653            ):
7654                this = f"CASCADE COMPLETE {self._prev.text.upper()}"
7655                expression = self._parse_into()
7656
7657        return self.expression(exp.AnalyzeValidate, kind=kind, this=this, expression=expression)
7658
7659    def _parse_analyze_columns(self) -> t.Optional[exp.AnalyzeColumns]:
7660        this = self._prev.text.upper()
7661        if self._match_text_seq("COLUMNS"):
7662            return self.expression(exp.AnalyzeColumns, this=f"{this} {self._prev.text.upper()}")
7663        return None
7664
7665    def _parse_analyze_delete(self) -> t.Optional[exp.AnalyzeDelete]:
7666        kind = self._prev.text.upper() if self._match_text_seq("SYSTEM") else None
7667        if self._match_text_seq("STATISTICS"):
7668            return self.expression(exp.AnalyzeDelete, kind=kind)
7669        return None
7670
7671    def _parse_analyze_list(self) -> t.Optional[exp.AnalyzeListChainedRows]:
7672        if self._match_text_seq("CHAINED", "ROWS"):
7673            return self.expression(exp.AnalyzeListChainedRows, expression=self._parse_into())
7674        return None
7675
7676    # https://dev.mysql.com/doc/refman/8.4/en/analyze-table.html
7677    def _parse_analyze_histogram(self) -> exp.AnalyzeHistogram:
7678        this = self._prev.text.upper()
7679        expression: t.Optional[exp.Expression] = None
7680        expressions = []
7681        update_options = None
7682
7683        if self._match_text_seq("HISTOGRAM", "ON"):
7684            expressions = self._parse_csv(self._parse_column_reference)
7685            with_expressions = []
7686            while self._match(TokenType.WITH):
7687                # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7688                if self._match_texts(("SYNC", "ASYNC")):
7689                    if self._match_text_seq("MODE", advance=False):
7690                        with_expressions.append(f"{self._prev.text.upper()} MODE")
7691                        self._advance()
7692                else:
7693                    buckets = self._parse_number()
7694                    if self._match_text_seq("BUCKETS"):
7695                        with_expressions.append(f"{buckets} BUCKETS")
7696            if with_expressions:
7697                expression = self.expression(exp.AnalyzeWith, expressions=with_expressions)
7698
7699            if self._match_texts(("MANUAL", "AUTO")) and self._match(
7700                TokenType.UPDATE, advance=False
7701            ):
7702                update_options = self._prev.text.upper()
7703                self._advance()
7704            elif self._match_text_seq("USING", "DATA"):
7705                expression = self.expression(exp.UsingData, this=self._parse_string())
7706
7707        return self.expression(
7708            exp.AnalyzeHistogram,
7709            this=this,
7710            expressions=expressions,
7711            expression=expression,
7712            update_options=update_options,
7713        )
7714
7715    def _parse_merge(self) -> exp.Merge:
7716        self._match(TokenType.INTO)
7717        target = self._parse_table()
7718
7719        if target and self._match(TokenType.ALIAS, advance=False):
7720            target.set("alias", self._parse_table_alias())
7721
7722        self._match(TokenType.USING)
7723        using = self._parse_table()
7724
7725        self._match(TokenType.ON)
7726        on = self._parse_assignment()
7727
7728        return self.expression(
7729            exp.Merge,
7730            this=target,
7731            using=using,
7732            on=on,
7733            whens=self._parse_when_matched(),
7734            returning=self._parse_returning(),
7735        )
7736
7737    def _parse_when_matched(self) -> exp.Whens:
7738        whens = []
7739
7740        while self._match(TokenType.WHEN):
7741            matched = not self._match(TokenType.NOT)
7742            self._match_text_seq("MATCHED")
7743            source = (
7744                False
7745                if self._match_text_seq("BY", "TARGET")
7746                else self._match_text_seq("BY", "SOURCE")
7747            )
7748            condition = self._parse_assignment() if self._match(TokenType.AND) else None
7749
7750            self._match(TokenType.THEN)
7751
7752            if self._match(TokenType.INSERT):
7753                this = self._parse_star()
7754                if this:
7755                    then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=this)
7756                else:
7757                    then = self.expression(
7758                        exp.Insert,
7759                        this=exp.var("ROW")
7760                        if self._match_text_seq("ROW")
7761                        else self._parse_value(values=False),
7762                        expression=self._match_text_seq("VALUES") and self._parse_value(),
7763                    )
7764            elif self._match(TokenType.UPDATE):
7765                expressions = self._parse_star()
7766                if expressions:
7767                    then = self.expression(exp.Update, expressions=expressions)
7768                else:
7769                    then = self.expression(
7770                        exp.Update,
7771                        expressions=self._match(TokenType.SET)
7772                        and self._parse_csv(self._parse_equality),
7773                    )
7774            elif self._match(TokenType.DELETE):
7775                then = self.expression(exp.Var, this=self._prev.text)
7776            else:
7777                then = self._parse_var_from_options(self.CONFLICT_ACTIONS)
7778
7779            whens.append(
7780                self.expression(
7781                    exp.When,
7782                    matched=matched,
7783                    source=source,
7784                    condition=condition,
7785                    then=then,
7786                )
7787            )
7788        return self.expression(exp.Whens, expressions=whens)
7789
7790    def _parse_show(self) -> t.Optional[exp.Expression]:
7791        parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE)
7792        if parser:
7793            return parser(self)
7794        return self._parse_as_command(self._prev)
7795
7796    def _parse_set_item_assignment(
7797        self, kind: t.Optional[str] = None
7798    ) -> t.Optional[exp.Expression]:
7799        index = self._index
7800
7801        if kind in ("GLOBAL", "SESSION") and self._match_text_seq("TRANSACTION"):
7802            return self._parse_set_transaction(global_=kind == "GLOBAL")
7803
7804        left = self._parse_primary() or self._parse_column()
7805        assignment_delimiter = self._match_texts(("=", "TO"))
7806
7807        if not left or (self.SET_REQUIRES_ASSIGNMENT_DELIMITER and not assignment_delimiter):
7808            self._retreat(index)
7809            return None
7810
7811        right = self._parse_statement() or self._parse_id_var()
7812        if isinstance(right, (exp.Column, exp.Identifier)):
7813            right = exp.var(right.name)
7814
7815        this = self.expression(exp.EQ, this=left, expression=right)
7816        return self.expression(exp.SetItem, this=this, kind=kind)
7817
7818    def _parse_set_transaction(self, global_: bool = False) -> exp.Expression:
7819        self._match_text_seq("TRANSACTION")
7820        characteristics = self._parse_csv(
7821            lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS)
7822        )
7823        return self.expression(
7824            exp.SetItem,
7825            expressions=characteristics,
7826            kind="TRANSACTION",
7827            **{"global": global_},  # type: ignore
7828        )
7829
7830    def _parse_set_item(self) -> t.Optional[exp.Expression]:
7831        parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE)
7832        return parser(self) if parser else self._parse_set_item_assignment(kind=None)
7833
7834    def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command:
7835        index = self._index
7836        set_ = self.expression(
7837            exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag
7838        )
7839
7840        if self._curr:
7841            self._retreat(index)
7842            return self._parse_as_command(self._prev)
7843
7844        return set_
7845
7846    def _parse_var_from_options(
7847        self, options: OPTIONS_TYPE, raise_unmatched: bool = True
7848    ) -> t.Optional[exp.Var]:
7849        start = self._curr
7850        if not start:
7851            return None
7852
7853        option = start.text.upper()
7854        continuations = options.get(option)
7855
7856        index = self._index
7857        self._advance()
7858        for keywords in continuations or []:
7859            if isinstance(keywords, str):
7860                keywords = (keywords,)
7861
7862            if self._match_text_seq(*keywords):
7863                option = f"{option} {' '.join(keywords)}"
7864                break
7865        else:
7866            if continuations or continuations is None:
7867                if raise_unmatched:
7868                    self.raise_error(f"Unknown option {option}")
7869
7870                self._retreat(index)
7871                return None
7872
7873        return exp.var(option)
7874
7875    def _parse_as_command(self, start: Token) -> exp.Command:
7876        while self._curr:
7877            self._advance()
7878        text = self._find_sql(start, self._prev)
7879        size = len(start.text)
7880        self._warn_unsupported()
7881        return exp.Command(this=text[:size], expression=text[size:])
7882
7883    def _parse_dict_property(self, this: str) -> exp.DictProperty:
7884        settings = []
7885
7886        self._match_l_paren()
7887        kind = self._parse_id_var()
7888
7889        if self._match(TokenType.L_PAREN):
7890            while True:
7891                key = self._parse_id_var()
7892                value = self._parse_primary()
7893                if not key and value is None:
7894                    break
7895                settings.append(self.expression(exp.DictSubProperty, this=key, value=value))
7896            self._match(TokenType.R_PAREN)
7897
7898        self._match_r_paren()
7899
7900        return self.expression(
7901            exp.DictProperty,
7902            this=this,
7903            kind=kind.this if kind else None,
7904            settings=settings,
7905        )
7906
7907    def _parse_dict_range(self, this: str) -> exp.DictRange:
7908        self._match_l_paren()
7909        has_min = self._match_text_seq("MIN")
7910        if has_min:
7911            min = self._parse_var() or self._parse_primary()
7912            self._match_text_seq("MAX")
7913            max = self._parse_var() or self._parse_primary()
7914        else:
7915            max = self._parse_var() or self._parse_primary()
7916            min = exp.Literal.number(0)
7917        self._match_r_paren()
7918        return self.expression(exp.DictRange, this=this, min=min, max=max)
7919
7920    def _parse_comprehension(
7921        self, this: t.Optional[exp.Expression]
7922    ) -> t.Optional[exp.Comprehension]:
7923        index = self._index
7924        expression = self._parse_column()
7925        if not self._match(TokenType.IN):
7926            self._retreat(index - 1)
7927            return None
7928        iterator = self._parse_column()
7929        condition = self._parse_assignment() if self._match_text_seq("IF") else None
7930        return self.expression(
7931            exp.Comprehension,
7932            this=this,
7933            expression=expression,
7934            iterator=iterator,
7935            condition=condition,
7936        )
7937
7938    def _parse_heredoc(self) -> t.Optional[exp.Heredoc]:
7939        if self._match(TokenType.HEREDOC_STRING):
7940            return self.expression(exp.Heredoc, this=self._prev.text)
7941
7942        if not self._match_text_seq("$"):
7943            return None
7944
7945        tags = ["$"]
7946        tag_text = None
7947
7948        if self._is_connected():
7949            self._advance()
7950            tags.append(self._prev.text.upper())
7951        else:
7952            self.raise_error("No closing $ found")
7953
7954        if tags[-1] != "$":
7955            if self._is_connected() and self._match_text_seq("$"):
7956                tag_text = tags[-1]
7957                tags.append("$")
7958            else:
7959                self.raise_error("No closing $ found")
7960
7961        heredoc_start = self._curr
7962
7963        while self._curr:
7964            if self._match_text_seq(*tags, advance=False):
7965                this = self._find_sql(heredoc_start, self._prev)
7966                self._advance(len(tags))
7967                return self.expression(exp.Heredoc, this=this, tag=tag_text)
7968
7969            self._advance()
7970
7971        self.raise_error(f"No closing {''.join(tags)} found")
7972        return None
7973
7974    def _find_parser(
7975        self, parsers: t.Dict[str, t.Callable], trie: t.Dict
7976    ) -> t.Optional[t.Callable]:
7977        if not self._curr:
7978            return None
7979
7980        index = self._index
7981        this = []
7982        while True:
7983            # The current token might be multiple words
7984            curr = self._curr.text.upper()
7985            key = curr.split(" ")
7986            this.append(curr)
7987
7988            self._advance()
7989            result, trie = in_trie(trie, key)
7990            if result == TrieResult.FAILED:
7991                break
7992
7993            if result == TrieResult.EXISTS:
7994                subparser = parsers[" ".join(this)]
7995                return subparser
7996
7997        self._retreat(index)
7998        return None
7999
8000    def _match(self, token_type, advance=True, expression=None):
8001        if not self._curr:
8002            return None
8003
8004        if self._curr.token_type == token_type:
8005            if advance:
8006                self._advance()
8007            self._add_comments(expression)
8008            return True
8009
8010        return None
8011
8012    def _match_set(self, types, advance=True):
8013        if not self._curr:
8014            return None
8015
8016        if self._curr.token_type in types:
8017            if advance:
8018                self._advance()
8019            return True
8020
8021        return None
8022
8023    def _match_pair(self, token_type_a, token_type_b, advance=True):
8024        if not self._curr or not self._next:
8025            return None
8026
8027        if self._curr.token_type == token_type_a and self._next.token_type == token_type_b:
8028            if advance:
8029                self._advance(2)
8030            return True
8031
8032        return None
8033
8034    def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
8035        if not self._match(TokenType.L_PAREN, expression=expression):
8036            self.raise_error("Expecting (")
8037
8038    def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
8039        if not self._match(TokenType.R_PAREN, expression=expression):
8040            self.raise_error("Expecting )")
8041
8042    def _match_texts(self, texts, advance=True):
8043        if (
8044            self._curr
8045            and self._curr.token_type != TokenType.STRING
8046            and self._curr.text.upper() in texts
8047        ):
8048            if advance:
8049                self._advance()
8050            return True
8051        return None
8052
8053    def _match_text_seq(self, *texts, advance=True):
8054        index = self._index
8055        for text in texts:
8056            if (
8057                self._curr
8058                and self._curr.token_type != TokenType.STRING
8059                and self._curr.text.upper() == text
8060            ):
8061                self._advance()
8062            else:
8063                self._retreat(index)
8064                return None
8065
8066        if not advance:
8067            self._retreat(index)
8068
8069        return True
8070
8071    def _replace_lambda(
8072        self, node: t.Optional[exp.Expression], expressions: t.List[exp.Expression]
8073    ) -> t.Optional[exp.Expression]:
8074        if not node:
8075            return node
8076
8077        lambda_types = {e.name: e.args.get("to") or False for e in expressions}
8078
8079        for column in node.find_all(exp.Column):
8080            typ = lambda_types.get(column.parts[0].name)
8081            if typ is not None:
8082                dot_or_id = column.to_dot() if column.table else column.this
8083
8084                if typ:
8085                    dot_or_id = self.expression(
8086                        exp.Cast,
8087                        this=dot_or_id,
8088                        to=typ,
8089                    )
8090
8091                parent = column.parent
8092
8093                while isinstance(parent, exp.Dot):
8094                    if not isinstance(parent.parent, exp.Dot):
8095                        parent.replace(dot_or_id)
8096                        break
8097                    parent = parent.parent
8098                else:
8099                    if column is node:
8100                        node = dot_or_id
8101                    else:
8102                        column.replace(dot_or_id)
8103        return node
8104
8105    def _parse_truncate_table(self) -> t.Optional[exp.TruncateTable] | exp.Expression:
8106        start = self._prev
8107
8108        # Not to be confused with TRUNCATE(number, decimals) function call
8109        if self._match(TokenType.L_PAREN):
8110            self._retreat(self._index - 2)
8111            return self._parse_function()
8112
8113        # Clickhouse supports TRUNCATE DATABASE as well
8114        is_database = self._match(TokenType.DATABASE)
8115
8116        self._match(TokenType.TABLE)
8117
8118        exists = self._parse_exists(not_=False)
8119
8120        expressions = self._parse_csv(
8121            lambda: self._parse_table(schema=True, is_db_reference=is_database)
8122        )
8123
8124        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
8125
8126        if self._match_text_seq("RESTART", "IDENTITY"):
8127            identity = "RESTART"
8128        elif self._match_text_seq("CONTINUE", "IDENTITY"):
8129            identity = "CONTINUE"
8130        else:
8131            identity = None
8132
8133        if self._match_text_seq("CASCADE") or self._match_text_seq("RESTRICT"):
8134            option = self._prev.text
8135        else:
8136            option = None
8137
8138        partition = self._parse_partition()
8139
8140        # Fallback case
8141        if self._curr:
8142            return self._parse_as_command(start)
8143
8144        return self.expression(
8145            exp.TruncateTable,
8146            expressions=expressions,
8147            is_database=is_database,
8148            exists=exists,
8149            cluster=cluster,
8150            identity=identity,
8151            option=option,
8152            partition=partition,
8153        )
8154
8155    def _parse_with_operator(self) -> t.Optional[exp.Expression]:
8156        this = self._parse_ordered(self._parse_opclass)
8157
8158        if not self._match(TokenType.WITH):
8159            return this
8160
8161        op = self._parse_var(any_token=True)
8162
8163        return self.expression(exp.WithOperator, this=this, op=op)
8164
8165    def _parse_wrapped_options(self) -> t.List[t.Optional[exp.Expression]]:
8166        self._match(TokenType.EQ)
8167        self._match(TokenType.L_PAREN)
8168
8169        opts: t.List[t.Optional[exp.Expression]] = []
8170        option: exp.Expression | None
8171        while self._curr and not self._match(TokenType.R_PAREN):
8172            if self._match_text_seq("FORMAT_NAME", "="):
8173                # The FORMAT_NAME can be set to an identifier for Snowflake and T-SQL
8174                option = self._parse_format_name()
8175            else:
8176                option = self._parse_property()
8177
8178            if option is None:
8179                self.raise_error("Unable to parse option")
8180                break
8181
8182            opts.append(option)
8183
8184        return opts
8185
8186    def _parse_copy_parameters(self) -> t.List[exp.CopyParameter]:
8187        sep = TokenType.COMMA if self.dialect.COPY_PARAMS_ARE_CSV else None
8188
8189        options = []
8190        while self._curr and not self._match(TokenType.R_PAREN, advance=False):
8191            option = self._parse_var(any_token=True)
8192            prev = self._prev.text.upper()
8193
8194            # Different dialects might separate options and values by white space, "=" and "AS"
8195            self._match(TokenType.EQ)
8196            self._match(TokenType.ALIAS)
8197
8198            param = self.expression(exp.CopyParameter, this=option)
8199
8200            if prev in self.COPY_INTO_VARLEN_OPTIONS and self._match(
8201                TokenType.L_PAREN, advance=False
8202            ):
8203                # Snowflake FILE_FORMAT case, Databricks COPY & FORMAT options
8204                param.set("expressions", self._parse_wrapped_options())
8205            elif prev == "FILE_FORMAT":
8206                # T-SQL's external file format case
8207                param.set("expression", self._parse_field())
8208            else:
8209                param.set("expression", self._parse_unquoted_field())
8210
8211            options.append(param)
8212            self._match(sep)
8213
8214        return options
8215
8216    def _parse_credentials(self) -> t.Optional[exp.Credentials]:
8217        expr = self.expression(exp.Credentials)
8218
8219        if self._match_text_seq("STORAGE_INTEGRATION", "="):
8220            expr.set("storage", self._parse_field())
8221        if self._match_text_seq("CREDENTIALS"):
8222            # Snowflake case: CREDENTIALS = (...), Redshift case: CREDENTIALS <string>
8223            creds = (
8224                self._parse_wrapped_options() if self._match(TokenType.EQ) else self._parse_field()
8225            )
8226            expr.set("credentials", creds)
8227        if self._match_text_seq("ENCRYPTION"):
8228            expr.set("encryption", self._parse_wrapped_options())
8229        if self._match_text_seq("IAM_ROLE"):
8230            expr.set("iam_role", self._parse_field())
8231        if self._match_text_seq("REGION"):
8232            expr.set("region", self._parse_field())
8233
8234        return expr
8235
8236    def _parse_file_location(self) -> t.Optional[exp.Expression]:
8237        return self._parse_field()
8238
8239    def _parse_copy(self) -> exp.Copy | exp.Command:
8240        start = self._prev
8241
8242        self._match(TokenType.INTO)
8243
8244        this = (
8245            self._parse_select(nested=True, parse_subquery_alias=False)
8246            if self._match(TokenType.L_PAREN, advance=False)
8247            else self._parse_table(schema=True)
8248        )
8249
8250        kind = self._match(TokenType.FROM) or not self._match_text_seq("TO")
8251
8252        files = self._parse_csv(self._parse_file_location)
8253        credentials = self._parse_credentials()
8254
8255        self._match_text_seq("WITH")
8256
8257        params = self._parse_wrapped(self._parse_copy_parameters, optional=True)
8258
8259        # Fallback case
8260        if self._curr:
8261            return self._parse_as_command(start)
8262
8263        return self.expression(
8264            exp.Copy,
8265            this=this,
8266            kind=kind,
8267            credentials=credentials,
8268            files=files,
8269            params=params,
8270        )
8271
8272    def _parse_normalize(self) -> exp.Normalize:
8273        return self.expression(
8274            exp.Normalize,
8275            this=self._parse_bitwise(),
8276            form=self._match(TokenType.COMMA) and self._parse_var(),
8277        )
8278
8279    def _parse_ceil_floor(self, expr_type: t.Type[TCeilFloor]) -> TCeilFloor:
8280        args = self._parse_csv(lambda: self._parse_lambda())
8281
8282        this = seq_get(args, 0)
8283        decimals = seq_get(args, 1)
8284
8285        return expr_type(
8286            this=this, decimals=decimals, to=self._match_text_seq("TO") and self._parse_var()
8287        )
8288
8289    def _parse_star_ops(self) -> t.Optional[exp.Expression]:
8290        star_token = self._prev
8291
8292        if self._match_text_seq("COLUMNS", "(", advance=False):
8293            this = self._parse_function()
8294            if isinstance(this, exp.Columns):
8295                this.set("unpack", True)
8296            return this
8297
8298        return self.expression(
8299            exp.Star,
8300            **{  # type: ignore
8301                "except": self._parse_star_op("EXCEPT", "EXCLUDE"),
8302                "replace": self._parse_star_op("REPLACE"),
8303                "rename": self._parse_star_op("RENAME"),
8304            },
8305        ).update_positions(star_token)
8306
8307    def _parse_grant_privilege(self) -> t.Optional[exp.GrantPrivilege]:
8308        privilege_parts = []
8309
8310        # Keep consuming consecutive keywords until comma (end of this privilege) or ON
8311        # (end of privilege list) or L_PAREN (start of column list) are met
8312        while self._curr and not self._match_set(self.PRIVILEGE_FOLLOW_TOKENS, advance=False):
8313            privilege_parts.append(self._curr.text.upper())
8314            self._advance()
8315
8316        this = exp.var(" ".join(privilege_parts))
8317        expressions = (
8318            self._parse_wrapped_csv(self._parse_column)
8319            if self._match(TokenType.L_PAREN, advance=False)
8320            else None
8321        )
8322
8323        return self.expression(exp.GrantPrivilege, this=this, expressions=expressions)
8324
8325    def _parse_grant_principal(self) -> t.Optional[exp.GrantPrincipal]:
8326        kind = self._match_texts(("ROLE", "GROUP")) and self._prev.text.upper()
8327        principal = self._parse_id_var()
8328
8329        if not principal:
8330            return None
8331
8332        return self.expression(exp.GrantPrincipal, this=principal, kind=kind)
8333
8334    def _parse_grant(self) -> exp.Grant | exp.Command:
8335        start = self._prev
8336
8337        privileges = self._parse_csv(self._parse_grant_privilege)
8338
8339        self._match(TokenType.ON)
8340        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
8341
8342        # Attempt to parse the securable e.g. MySQL allows names
8343        # such as "foo.*", "*.*" which are not easily parseable yet
8344        securable = self._try_parse(self._parse_table_parts)
8345
8346        if not securable or not self._match_text_seq("TO"):
8347            return self._parse_as_command(start)
8348
8349        principals = self._parse_csv(self._parse_grant_principal)
8350
8351        grant_option = self._match_text_seq("WITH", "GRANT", "OPTION")
8352
8353        if self._curr:
8354            return self._parse_as_command(start)
8355
8356        return self.expression(
8357            exp.Grant,
8358            privileges=privileges,
8359            kind=kind,
8360            securable=securable,
8361            principals=principals,
8362            grant_option=grant_option,
8363        )
8364
8365    def _parse_overlay(self) -> exp.Overlay:
8366        return self.expression(
8367            exp.Overlay,
8368            **{  # type: ignore
8369                "this": self._parse_bitwise(),
8370                "expression": self._match_text_seq("PLACING") and self._parse_bitwise(),
8371                "from": self._match_text_seq("FROM") and self._parse_bitwise(),
8372                "for": self._match_text_seq("FOR") and self._parse_bitwise(),
8373            },
8374        )
8375
8376    def _parse_format_name(self) -> exp.Property:
8377        # Note: Although not specified in the docs, Snowflake does accept a string/identifier
8378        # for FILE_FORMAT = <format_name>
8379        return self.expression(
8380            exp.Property,
8381            this=exp.var("FORMAT_NAME"),
8382            value=self._parse_string() or self._parse_table_parts(),
8383        )
8384
8385    def _parse_max_min_by(self, expr_type: t.Type[exp.AggFunc]) -> exp.AggFunc:
8386        args: t.List[exp.Expression] = []
8387
8388        if self._match(TokenType.DISTINCT):
8389            args.append(self.expression(exp.Distinct, expressions=[self._parse_assignment()]))
8390            self._match(TokenType.COMMA)
8391
8392        args.extend(self._parse_csv(self._parse_assignment))
8393
8394        return self.expression(
8395            expr_type, this=seq_get(args, 0), expression=seq_get(args, 1), count=seq_get(args, 2)
8396        )
8397
8398    def _identifier_expression(
8399        self, token: t.Optional[Token] = None, **kwargs: t.Any
8400    ) -> exp.Identifier:
8401        token = token or self._prev
8402        expression = self.expression(exp.Identifier, this=token.text, **kwargs)
8403        expression.update_positions(token)
8404        return expression
8405
8406    def _build_pipe_cte(
8407        self,
8408        query: exp.Query,
8409        expressions: t.List[exp.Expression],
8410        alias_cte: t.Optional[exp.TableAlias] = None,
8411    ) -> exp.Select:
8412        new_cte: t.Optional[t.Union[str, exp.TableAlias]]
8413        if alias_cte:
8414            new_cte = alias_cte
8415        else:
8416            self._pipe_cte_counter += 1
8417            new_cte = f"__tmp{self._pipe_cte_counter}"
8418
8419        with_ = query.args.get("with")
8420        ctes = with_.pop() if with_ else None
8421
8422        new_select = exp.select(*expressions, copy=False).from_(new_cte, copy=False)
8423        if ctes:
8424            new_select.set("with", ctes)
8425
8426        return new_select.with_(new_cte, as_=query, copy=False)
8427
8428    def _parse_pipe_syntax_select(self, query: exp.Select) -> exp.Select:
8429        select = self._parse_select(consume_pipe=False)
8430        if not select:
8431            return query
8432
8433        return self._build_pipe_cte(
8434            query=query.select(*select.expressions, append=False), expressions=[exp.Star()]
8435        )
8436
8437    def _parse_pipe_syntax_limit(self, query: exp.Select) -> exp.Select:
8438        limit = self._parse_limit()
8439        offset = self._parse_offset()
8440        if limit:
8441            curr_limit = query.args.get("limit", limit)
8442            if curr_limit.expression.to_py() >= limit.expression.to_py():
8443                query.limit(limit, copy=False)
8444        if offset:
8445            curr_offset = query.args.get("offset")
8446            curr_offset = curr_offset.expression.to_py() if curr_offset else 0
8447            query.offset(exp.Literal.number(curr_offset + offset.expression.to_py()), copy=False)
8448
8449        return query
8450
8451    def _parse_pipe_syntax_aggregate_fields(self) -> t.Optional[exp.Expression]:
8452        this = self._parse_assignment()
8453        if self._match_text_seq("GROUP", "AND", advance=False):
8454            return this
8455
8456        this = self._parse_alias(this)
8457
8458        if self._match_set((TokenType.ASC, TokenType.DESC), advance=False):
8459            return self._parse_ordered(lambda: this)
8460
8461        return this
8462
8463    def _parse_pipe_syntax_aggregate_group_order_by(
8464        self, query: exp.Select, group_by_exists: bool = True
8465    ) -> exp.Select:
8466        expr = self._parse_csv(self._parse_pipe_syntax_aggregate_fields)
8467        aggregates_or_groups, orders = [], []
8468        for element in expr:
8469            if isinstance(element, exp.Ordered):
8470                this = element.this
8471                if isinstance(this, exp.Alias):
8472                    element.set("this", this.args["alias"])
8473                orders.append(element)
8474            else:
8475                this = element
8476            aggregates_or_groups.append(this)
8477
8478        if group_by_exists:
8479            query.select(*aggregates_or_groups, copy=False).group_by(
8480                *[projection.args.get("alias", projection) for projection in aggregates_or_groups],
8481                copy=False,
8482            )
8483        else:
8484            query.select(*aggregates_or_groups, append=False, copy=False)
8485
8486        if orders:
8487            return query.order_by(*orders, append=False, copy=False)
8488
8489        return query
8490
8491    def _parse_pipe_syntax_aggregate(self, query: exp.Select) -> exp.Select:
8492        self._match_text_seq("AGGREGATE")
8493        query = self._parse_pipe_syntax_aggregate_group_order_by(query, group_by_exists=False)
8494
8495        if self._match(TokenType.GROUP_BY) or (
8496            self._match_text_seq("GROUP", "AND") and self._match(TokenType.ORDER_BY)
8497        ):
8498            query = self._parse_pipe_syntax_aggregate_group_order_by(query)
8499
8500        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8501
8502    def _parse_pipe_syntax_set_operator(self, query: exp.Query) -> t.Optional[exp.Query]:
8503        first_setop = self.parse_set_operation(this=query)
8504        if not first_setop:
8505            return None
8506
8507        def _parse_and_unwrap_query() -> t.Optional[exp.Select]:
8508            expr = self._parse_paren()
8509            return expr.assert_is(exp.Subquery).unnest() if expr else None
8510
8511        first_setop.this.pop()
8512
8513        setops = [
8514            first_setop.expression.pop().assert_is(exp.Subquery).unnest(),
8515            *self._parse_csv(_parse_and_unwrap_query),
8516        ]
8517
8518        query = self._build_pipe_cte(query=query, expressions=[exp.Star()])
8519        with_ = query.args.get("with")
8520        ctes = with_.pop() if with_ else None
8521
8522        if isinstance(first_setop, exp.Union):
8523            query = query.union(*setops, copy=False, **first_setop.args)
8524        elif isinstance(first_setop, exp.Except):
8525            query = query.except_(*setops, copy=False, **first_setop.args)
8526        else:
8527            query = query.intersect(*setops, copy=False, **first_setop.args)
8528
8529        query.set("with", ctes)
8530
8531        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8532
8533    def _parse_pipe_syntax_join(self, query: exp.Query) -> t.Optional[exp.Query]:
8534        join = self._parse_join()
8535        if not join:
8536            return None
8537
8538        if isinstance(query, exp.Select):
8539            return query.join(join, copy=False)
8540
8541        return query
8542
8543    def _parse_pipe_syntax_pivot(self, query: exp.Select) -> exp.Select:
8544        pivots = self._parse_pivots()
8545        if not pivots:
8546            return query
8547
8548        from_ = query.args.get("from")
8549        if from_:
8550            from_.this.set("pivots", pivots)
8551
8552        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8553
8554    def _parse_pipe_syntax_extend(self, query: exp.Select) -> exp.Select:
8555        self._match_text_seq("EXTEND")
8556        query.select(*[exp.Star(), *self._parse_expressions()], append=False, copy=False)
8557        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8558
8559    def _parse_pipe_syntax_tablesample(self, query: exp.Select) -> exp.Select:
8560        sample = self._parse_table_sample()
8561
8562        with_ = query.args.get("with")
8563        if with_:
8564            with_.expressions[-1].this.set("sample", sample)
8565        else:
8566            query.set("sample", sample)
8567
8568        return query
8569
8570    def _parse_pipe_syntax_query(self, query: exp.Query) -> t.Optional[exp.Query]:
8571        if isinstance(query, exp.Subquery):
8572            query = exp.select("*").from_(query, copy=False)
8573
8574        if not query.args.get("from"):
8575            query = exp.select("*").from_(query.subquery(copy=False), copy=False)
8576
8577        while self._match(TokenType.PIPE_GT):
8578            start = self._curr
8579            parser = self.PIPE_SYNTAX_TRANSFORM_PARSERS.get(self._curr.text.upper())
8580            if not parser:
8581                # The set operators (UNION, etc) and the JOIN operator have a few common starting
8582                # keywords, making it tricky to disambiguate them without lookahead. The approach
8583                # here is to try and parse a set operation and if that fails, then try to parse a
8584                # join operator. If that fails as well, then the operator is not supported.
8585                parsed_query = self._parse_pipe_syntax_set_operator(query)
8586                parsed_query = parsed_query or self._parse_pipe_syntax_join(query)
8587                if not parsed_query:
8588                    self._retreat(start)
8589                    self.raise_error(f"Unsupported pipe syntax operator: '{start.text.upper()}'.")
8590                    break
8591                query = parsed_query
8592            else:
8593                query = parser(self, query)
8594
8595        return query
logger = <Logger sqlglot (WARNING)>
OPTIONS_TYPE = typing.Dict[str, typing.Sequence[typing.Union[typing.Sequence[str], str]]]
TIME_ZONE_RE: Pattern[str] = re.compile(':.*?[a-zA-Z\\+\\-]')
def build_var_map(args: List) -> sqlglot.expressions.StarMap | sqlglot.expressions.VarMap:
32def build_var_map(args: t.List) -> exp.StarMap | exp.VarMap:
33    if len(args) == 1 and args[0].is_star:
34        return exp.StarMap(this=args[0])
35
36    keys = []
37    values = []
38    for i in range(0, len(args), 2):
39        keys.append(args[i])
40        values.append(args[i + 1])
41
42    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:
45def build_like(args: t.List) -> exp.Escape | exp.Like:
46    like = exp.Like(this=seq_get(args, 1), expression=seq_get(args, 0))
47    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]]:
50def binary_range_parser(
51    expr_type: t.Type[exp.Expression], reverse_args: bool = False
52) -> t.Callable[[Parser, t.Optional[exp.Expression]], t.Optional[exp.Expression]]:
53    def _parse_binary_range(
54        self: Parser, this: t.Optional[exp.Expression]
55    ) -> t.Optional[exp.Expression]:
56        expression = self._parse_bitwise()
57        if reverse_args:
58            this, expression = expression, this
59        return self._parse_escape(self.expression(expr_type, this=this, expression=expression))
60
61    return _parse_binary_range
def build_logarithm( args: List, dialect: sqlglot.dialects.Dialect) -> sqlglot.expressions.Func:
64def build_logarithm(args: t.List, dialect: Dialect) -> exp.Func:
65    # Default argument order is base, expression
66    this = seq_get(args, 0)
67    expression = seq_get(args, 1)
68
69    if expression:
70        if not dialect.LOG_BASE_FIRST:
71            this, expression = expression, this
72        return exp.Log(this=this, expression=expression)
73
74    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:
77def build_hex(args: t.List, dialect: Dialect) -> exp.Hex | exp.LowerHex:
78    arg = seq_get(args, 0)
79    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:
82def build_lower(args: t.List) -> exp.Lower | exp.Hex:
83    # LOWER(HEX(..)) can be simplified to LowerHex to simplify its transpilation
84    arg = seq_get(args, 0)
85    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:
88def build_upper(args: t.List) -> exp.Upper | exp.Hex:
89    # UPPER(HEX(..)) can be simplified to Hex to simplify its transpilation
90    arg = seq_get(args, 0)
91    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]:
 94def build_extract_json_with_path(expr_type: t.Type[E]) -> t.Callable[[t.List, Dialect], E]:
 95    def _builder(args: t.List, dialect: Dialect) -> E:
 96        expression = expr_type(
 97            this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1))
 98        )
 99        if len(args) > 2 and expr_type is exp.JSONExtract:
100            expression.set("expressions", args[2:])
101
102        return expression
103
104    return _builder
def build_mod(args: List) -> sqlglot.expressions.Mod:
107def build_mod(args: t.List) -> exp.Mod:
108    this = seq_get(args, 0)
109    expression = seq_get(args, 1)
110
111    # Wrap the operands if they are binary nodes, e.g. MOD(a + 1, 7) -> (a + 1) % 7
112    this = exp.Paren(this=this) if isinstance(this, exp.Binary) else this
113    expression = exp.Paren(this=expression) if isinstance(expression, exp.Binary) else expression
114
115    return exp.Mod(this=this, expression=expression)
def build_pad(args: List, is_left: bool = True):
118def build_pad(args: t.List, is_left: bool = True):
119    return exp.Pad(
120        this=seq_get(args, 0),
121        expression=seq_get(args, 1),
122        fill_pattern=seq_get(args, 2),
123        is_left=is_left,
124    )
def build_array_constructor( exp_class: Type[~E], args: List, bracket_kind: sqlglot.tokens.TokenType, dialect: sqlglot.dialects.Dialect) -> sqlglot.expressions.Expression:
127def build_array_constructor(
128    exp_class: t.Type[E], args: t.List, bracket_kind: TokenType, dialect: Dialect
129) -> exp.Expression:
130    array_exp = exp_class(expressions=args)
131
132    if exp_class == exp.Array and dialect.HAS_DISTINCT_ARRAY_CONSTRUCTORS:
133        array_exp.set("bracket_notation", bracket_kind == TokenType.L_BRACKET)
134
135    return array_exp
def build_convert_timezone( args: List, default_source_tz: Optional[str] = None) -> Union[sqlglot.expressions.ConvertTimezone, sqlglot.expressions.Anonymous]:
138def build_convert_timezone(
139    args: t.List, default_source_tz: t.Optional[str] = None
140) -> t.Union[exp.ConvertTimezone, exp.Anonymous]:
141    if len(args) == 2:
142        source_tz = exp.Literal.string(default_source_tz) if default_source_tz else None
143        return exp.ConvertTimezone(
144            source_tz=source_tz, target_tz=seq_get(args, 0), timestamp=seq_get(args, 1)
145        )
146
147    return exp.ConvertTimezone.from_arg_list(args)
def build_trim(args: List, is_left: bool = True):
150def build_trim(args: t.List, is_left: bool = True):
151    return exp.Trim(
152        this=seq_get(args, 0),
153        expression=seq_get(args, 1),
154        position="LEADING" if is_left else "TRAILING",
155    )
def build_coalesce( args: List, is_nvl: Optional[bool] = None, is_null: Optional[bool] = None) -> sqlglot.expressions.Coalesce:
158def build_coalesce(
159    args: t.List, is_nvl: t.Optional[bool] = None, is_null: t.Optional[bool] = None
160) -> exp.Coalesce:
161    return exp.Coalesce(this=seq_get(args, 0), expressions=args[1:], is_nvl=is_nvl, is_null=is_null)
def build_locate_strposition(args: List):
164def build_locate_strposition(args: t.List):
165    return exp.StrPosition(
166        this=seq_get(args, 1),
167        substr=seq_get(args, 0),
168        position=seq_get(args, 2),
169    )
class Parser:
 182class Parser(metaclass=_Parser):
 183    """
 184    Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.
 185
 186    Args:
 187        error_level: The desired error level.
 188            Default: ErrorLevel.IMMEDIATE
 189        error_message_context: The amount of context to capture from a query string when displaying
 190            the error message (in number of characters).
 191            Default: 100
 192        max_errors: Maximum number of error messages to include in a raised ParseError.
 193            This is only relevant if error_level is ErrorLevel.RAISE.
 194            Default: 3
 195    """
 196
 197    FUNCTIONS: t.Dict[str, t.Callable] = {
 198        **{name: func.from_arg_list for name, func in exp.FUNCTION_BY_NAME.items()},
 199        **dict.fromkeys(("COALESCE", "IFNULL", "NVL"), build_coalesce),
 200        "ARRAY": lambda args, dialect: exp.Array(expressions=args),
 201        "ARRAYAGG": lambda args, dialect: exp.ArrayAgg(
 202            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 203        ),
 204        "ARRAY_AGG": lambda args, dialect: exp.ArrayAgg(
 205            this=seq_get(args, 0), nulls_excluded=dialect.ARRAY_AGG_INCLUDES_NULLS is None or None
 206        ),
 207        "CHAR": lambda args: exp.Chr(expressions=args),
 208        "CHR": lambda args: exp.Chr(expressions=args),
 209        "COUNT": lambda args: exp.Count(this=seq_get(args, 0), expressions=args[1:], big_int=True),
 210        "CONCAT": lambda args, dialect: exp.Concat(
 211            expressions=args,
 212            safe=not dialect.STRICT_STRING_CONCAT,
 213            coalesce=dialect.CONCAT_COALESCE,
 214        ),
 215        "CONCAT_WS": lambda args, dialect: exp.ConcatWs(
 216            expressions=args,
 217            safe=not dialect.STRICT_STRING_CONCAT,
 218            coalesce=dialect.CONCAT_COALESCE,
 219        ),
 220        "CONVERT_TIMEZONE": build_convert_timezone,
 221        "DATE_TO_DATE_STR": lambda args: exp.Cast(
 222            this=seq_get(args, 0),
 223            to=exp.DataType(this=exp.DataType.Type.TEXT),
 224        ),
 225        "GENERATE_DATE_ARRAY": lambda args: exp.GenerateDateArray(
 226            start=seq_get(args, 0),
 227            end=seq_get(args, 1),
 228            step=seq_get(args, 2) or exp.Interval(this=exp.Literal.string(1), unit=exp.var("DAY")),
 229        ),
 230        "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)),
 231        "HEX": build_hex,
 232        "JSON_EXTRACT": build_extract_json_with_path(exp.JSONExtract),
 233        "JSON_EXTRACT_SCALAR": build_extract_json_with_path(exp.JSONExtractScalar),
 234        "JSON_EXTRACT_PATH_TEXT": build_extract_json_with_path(exp.JSONExtractScalar),
 235        "LIKE": build_like,
 236        "LOG": build_logarithm,
 237        "LOG2": lambda args: exp.Log(this=exp.Literal.number(2), expression=seq_get(args, 0)),
 238        "LOG10": lambda args: exp.Log(this=exp.Literal.number(10), expression=seq_get(args, 0)),
 239        "LOWER": build_lower,
 240        "LPAD": lambda args: build_pad(args),
 241        "LEFTPAD": lambda args: build_pad(args),
 242        "LTRIM": lambda args: build_trim(args),
 243        "MOD": build_mod,
 244        "RIGHTPAD": lambda args: build_pad(args, is_left=False),
 245        "RPAD": lambda args: build_pad(args, is_left=False),
 246        "RTRIM": lambda args: build_trim(args, is_left=False),
 247        "SCOPE_RESOLUTION": lambda args: exp.ScopeResolution(expression=seq_get(args, 0))
 248        if len(args) != 2
 249        else exp.ScopeResolution(this=seq_get(args, 0), expression=seq_get(args, 1)),
 250        "STRPOS": exp.StrPosition.from_arg_list,
 251        "CHARINDEX": lambda args: build_locate_strposition(args),
 252        "INSTR": exp.StrPosition.from_arg_list,
 253        "LOCATE": lambda args: build_locate_strposition(args),
 254        "TIME_TO_TIME_STR": lambda args: exp.Cast(
 255            this=seq_get(args, 0),
 256            to=exp.DataType(this=exp.DataType.Type.TEXT),
 257        ),
 258        "TO_HEX": build_hex,
 259        "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring(
 260            this=exp.Cast(
 261                this=seq_get(args, 0),
 262                to=exp.DataType(this=exp.DataType.Type.TEXT),
 263            ),
 264            start=exp.Literal.number(1),
 265            length=exp.Literal.number(10),
 266        ),
 267        "UNNEST": lambda args: exp.Unnest(expressions=ensure_list(seq_get(args, 0))),
 268        "UPPER": build_upper,
 269        "VAR_MAP": build_var_map,
 270    }
 271
 272    NO_PAREN_FUNCTIONS = {
 273        TokenType.CURRENT_DATE: exp.CurrentDate,
 274        TokenType.CURRENT_DATETIME: exp.CurrentDate,
 275        TokenType.CURRENT_TIME: exp.CurrentTime,
 276        TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp,
 277        TokenType.CURRENT_USER: exp.CurrentUser,
 278    }
 279
 280    STRUCT_TYPE_TOKENS = {
 281        TokenType.NESTED,
 282        TokenType.OBJECT,
 283        TokenType.STRUCT,
 284        TokenType.UNION,
 285    }
 286
 287    NESTED_TYPE_TOKENS = {
 288        TokenType.ARRAY,
 289        TokenType.LIST,
 290        TokenType.LOWCARDINALITY,
 291        TokenType.MAP,
 292        TokenType.NULLABLE,
 293        TokenType.RANGE,
 294        *STRUCT_TYPE_TOKENS,
 295    }
 296
 297    ENUM_TYPE_TOKENS = {
 298        TokenType.DYNAMIC,
 299        TokenType.ENUM,
 300        TokenType.ENUM8,
 301        TokenType.ENUM16,
 302    }
 303
 304    AGGREGATE_TYPE_TOKENS = {
 305        TokenType.AGGREGATEFUNCTION,
 306        TokenType.SIMPLEAGGREGATEFUNCTION,
 307    }
 308
 309    TYPE_TOKENS = {
 310        TokenType.BIT,
 311        TokenType.BOOLEAN,
 312        TokenType.TINYINT,
 313        TokenType.UTINYINT,
 314        TokenType.SMALLINT,
 315        TokenType.USMALLINT,
 316        TokenType.INT,
 317        TokenType.UINT,
 318        TokenType.BIGINT,
 319        TokenType.UBIGINT,
 320        TokenType.INT128,
 321        TokenType.UINT128,
 322        TokenType.INT256,
 323        TokenType.UINT256,
 324        TokenType.MEDIUMINT,
 325        TokenType.UMEDIUMINT,
 326        TokenType.FIXEDSTRING,
 327        TokenType.FLOAT,
 328        TokenType.DOUBLE,
 329        TokenType.UDOUBLE,
 330        TokenType.CHAR,
 331        TokenType.NCHAR,
 332        TokenType.VARCHAR,
 333        TokenType.NVARCHAR,
 334        TokenType.BPCHAR,
 335        TokenType.TEXT,
 336        TokenType.MEDIUMTEXT,
 337        TokenType.LONGTEXT,
 338        TokenType.BLOB,
 339        TokenType.MEDIUMBLOB,
 340        TokenType.LONGBLOB,
 341        TokenType.BINARY,
 342        TokenType.VARBINARY,
 343        TokenType.JSON,
 344        TokenType.JSONB,
 345        TokenType.INTERVAL,
 346        TokenType.TINYBLOB,
 347        TokenType.TINYTEXT,
 348        TokenType.TIME,
 349        TokenType.TIMETZ,
 350        TokenType.TIMESTAMP,
 351        TokenType.TIMESTAMP_S,
 352        TokenType.TIMESTAMP_MS,
 353        TokenType.TIMESTAMP_NS,
 354        TokenType.TIMESTAMPTZ,
 355        TokenType.TIMESTAMPLTZ,
 356        TokenType.TIMESTAMPNTZ,
 357        TokenType.DATETIME,
 358        TokenType.DATETIME2,
 359        TokenType.DATETIME64,
 360        TokenType.SMALLDATETIME,
 361        TokenType.DATE,
 362        TokenType.DATE32,
 363        TokenType.INT4RANGE,
 364        TokenType.INT4MULTIRANGE,
 365        TokenType.INT8RANGE,
 366        TokenType.INT8MULTIRANGE,
 367        TokenType.NUMRANGE,
 368        TokenType.NUMMULTIRANGE,
 369        TokenType.TSRANGE,
 370        TokenType.TSMULTIRANGE,
 371        TokenType.TSTZRANGE,
 372        TokenType.TSTZMULTIRANGE,
 373        TokenType.DATERANGE,
 374        TokenType.DATEMULTIRANGE,
 375        TokenType.DECIMAL,
 376        TokenType.DECIMAL32,
 377        TokenType.DECIMAL64,
 378        TokenType.DECIMAL128,
 379        TokenType.DECIMAL256,
 380        TokenType.UDECIMAL,
 381        TokenType.BIGDECIMAL,
 382        TokenType.UUID,
 383        TokenType.GEOGRAPHY,
 384        TokenType.GEOMETRY,
 385        TokenType.POINT,
 386        TokenType.RING,
 387        TokenType.LINESTRING,
 388        TokenType.MULTILINESTRING,
 389        TokenType.POLYGON,
 390        TokenType.MULTIPOLYGON,
 391        TokenType.HLLSKETCH,
 392        TokenType.HSTORE,
 393        TokenType.PSEUDO_TYPE,
 394        TokenType.SUPER,
 395        TokenType.SERIAL,
 396        TokenType.SMALLSERIAL,
 397        TokenType.BIGSERIAL,
 398        TokenType.XML,
 399        TokenType.YEAR,
 400        TokenType.USERDEFINED,
 401        TokenType.MONEY,
 402        TokenType.SMALLMONEY,
 403        TokenType.ROWVERSION,
 404        TokenType.IMAGE,
 405        TokenType.VARIANT,
 406        TokenType.VECTOR,
 407        TokenType.VOID,
 408        TokenType.OBJECT,
 409        TokenType.OBJECT_IDENTIFIER,
 410        TokenType.INET,
 411        TokenType.IPADDRESS,
 412        TokenType.IPPREFIX,
 413        TokenType.IPV4,
 414        TokenType.IPV6,
 415        TokenType.UNKNOWN,
 416        TokenType.NOTHING,
 417        TokenType.NULL,
 418        TokenType.NAME,
 419        TokenType.TDIGEST,
 420        TokenType.DYNAMIC,
 421        *ENUM_TYPE_TOKENS,
 422        *NESTED_TYPE_TOKENS,
 423        *AGGREGATE_TYPE_TOKENS,
 424    }
 425
 426    SIGNED_TO_UNSIGNED_TYPE_TOKEN = {
 427        TokenType.BIGINT: TokenType.UBIGINT,
 428        TokenType.INT: TokenType.UINT,
 429        TokenType.MEDIUMINT: TokenType.UMEDIUMINT,
 430        TokenType.SMALLINT: TokenType.USMALLINT,
 431        TokenType.TINYINT: TokenType.UTINYINT,
 432        TokenType.DECIMAL: TokenType.UDECIMAL,
 433        TokenType.DOUBLE: TokenType.UDOUBLE,
 434    }
 435
 436    SUBQUERY_PREDICATES = {
 437        TokenType.ANY: exp.Any,
 438        TokenType.ALL: exp.All,
 439        TokenType.EXISTS: exp.Exists,
 440        TokenType.SOME: exp.Any,
 441    }
 442
 443    RESERVED_TOKENS = {
 444        *Tokenizer.SINGLE_TOKENS.values(),
 445        TokenType.SELECT,
 446    } - {TokenType.IDENTIFIER}
 447
 448    DB_CREATABLES = {
 449        TokenType.DATABASE,
 450        TokenType.DICTIONARY,
 451        TokenType.FILE_FORMAT,
 452        TokenType.MODEL,
 453        TokenType.NAMESPACE,
 454        TokenType.SCHEMA,
 455        TokenType.SEQUENCE,
 456        TokenType.SINK,
 457        TokenType.SOURCE,
 458        TokenType.STAGE,
 459        TokenType.STORAGE_INTEGRATION,
 460        TokenType.STREAMLIT,
 461        TokenType.TABLE,
 462        TokenType.TAG,
 463        TokenType.VIEW,
 464        TokenType.WAREHOUSE,
 465    }
 466
 467    CREATABLES = {
 468        TokenType.COLUMN,
 469        TokenType.CONSTRAINT,
 470        TokenType.FOREIGN_KEY,
 471        TokenType.FUNCTION,
 472        TokenType.INDEX,
 473        TokenType.PROCEDURE,
 474        *DB_CREATABLES,
 475    }
 476
 477    ALTERABLES = {
 478        TokenType.INDEX,
 479        TokenType.TABLE,
 480        TokenType.VIEW,
 481    }
 482
 483    # Tokens that can represent identifiers
 484    ID_VAR_TOKENS = {
 485        TokenType.ALL,
 486        TokenType.ATTACH,
 487        TokenType.VAR,
 488        TokenType.ANTI,
 489        TokenType.APPLY,
 490        TokenType.ASC,
 491        TokenType.ASOF,
 492        TokenType.AUTO_INCREMENT,
 493        TokenType.BEGIN,
 494        TokenType.BPCHAR,
 495        TokenType.CACHE,
 496        TokenType.CASE,
 497        TokenType.COLLATE,
 498        TokenType.COMMAND,
 499        TokenType.COMMENT,
 500        TokenType.COMMIT,
 501        TokenType.CONSTRAINT,
 502        TokenType.COPY,
 503        TokenType.CUBE,
 504        TokenType.CURRENT_SCHEMA,
 505        TokenType.DEFAULT,
 506        TokenType.DELETE,
 507        TokenType.DESC,
 508        TokenType.DESCRIBE,
 509        TokenType.DETACH,
 510        TokenType.DICTIONARY,
 511        TokenType.DIV,
 512        TokenType.END,
 513        TokenType.EXECUTE,
 514        TokenType.EXPORT,
 515        TokenType.ESCAPE,
 516        TokenType.FALSE,
 517        TokenType.FIRST,
 518        TokenType.FILTER,
 519        TokenType.FINAL,
 520        TokenType.FORMAT,
 521        TokenType.FULL,
 522        TokenType.GET,
 523        TokenType.IDENTIFIER,
 524        TokenType.IS,
 525        TokenType.ISNULL,
 526        TokenType.INTERVAL,
 527        TokenType.KEEP,
 528        TokenType.KILL,
 529        TokenType.LEFT,
 530        TokenType.LIMIT,
 531        TokenType.LOAD,
 532        TokenType.MERGE,
 533        TokenType.NATURAL,
 534        TokenType.NEXT,
 535        TokenType.OFFSET,
 536        TokenType.OPERATOR,
 537        TokenType.ORDINALITY,
 538        TokenType.OVERLAPS,
 539        TokenType.OVERWRITE,
 540        TokenType.PARTITION,
 541        TokenType.PERCENT,
 542        TokenType.PIVOT,
 543        TokenType.PRAGMA,
 544        TokenType.PUT,
 545        TokenType.RANGE,
 546        TokenType.RECURSIVE,
 547        TokenType.REFERENCES,
 548        TokenType.REFRESH,
 549        TokenType.RENAME,
 550        TokenType.REPLACE,
 551        TokenType.RIGHT,
 552        TokenType.ROLLUP,
 553        TokenType.ROW,
 554        TokenType.ROWS,
 555        TokenType.SEMI,
 556        TokenType.SET,
 557        TokenType.SETTINGS,
 558        TokenType.SHOW,
 559        TokenType.TEMPORARY,
 560        TokenType.TOP,
 561        TokenType.TRUE,
 562        TokenType.TRUNCATE,
 563        TokenType.UNIQUE,
 564        TokenType.UNNEST,
 565        TokenType.UNPIVOT,
 566        TokenType.UPDATE,
 567        TokenType.USE,
 568        TokenType.VOLATILE,
 569        TokenType.WINDOW,
 570        *CREATABLES,
 571        *SUBQUERY_PREDICATES,
 572        *TYPE_TOKENS,
 573        *NO_PAREN_FUNCTIONS,
 574    }
 575    ID_VAR_TOKENS.remove(TokenType.UNION)
 576
 577    TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
 578        TokenType.ANTI,
 579        TokenType.APPLY,
 580        TokenType.ASOF,
 581        TokenType.FULL,
 582        TokenType.LEFT,
 583        TokenType.LOCK,
 584        TokenType.NATURAL,
 585        TokenType.RIGHT,
 586        TokenType.SEMI,
 587        TokenType.WINDOW,
 588    }
 589
 590    ALIAS_TOKENS = ID_VAR_TOKENS
 591
 592    COLON_PLACEHOLDER_TOKENS = ID_VAR_TOKENS
 593
 594    ARRAY_CONSTRUCTORS = {
 595        "ARRAY": exp.Array,
 596        "LIST": exp.List,
 597    }
 598
 599    COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS}
 600
 601    UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET}
 602
 603    TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"}
 604
 605    FUNC_TOKENS = {
 606        TokenType.COLLATE,
 607        TokenType.COMMAND,
 608        TokenType.CURRENT_DATE,
 609        TokenType.CURRENT_DATETIME,
 610        TokenType.CURRENT_SCHEMA,
 611        TokenType.CURRENT_TIMESTAMP,
 612        TokenType.CURRENT_TIME,
 613        TokenType.CURRENT_USER,
 614        TokenType.FILTER,
 615        TokenType.FIRST,
 616        TokenType.FORMAT,
 617        TokenType.GET,
 618        TokenType.GLOB,
 619        TokenType.IDENTIFIER,
 620        TokenType.INDEX,
 621        TokenType.ISNULL,
 622        TokenType.ILIKE,
 623        TokenType.INSERT,
 624        TokenType.LIKE,
 625        TokenType.MERGE,
 626        TokenType.NEXT,
 627        TokenType.OFFSET,
 628        TokenType.PRIMARY_KEY,
 629        TokenType.RANGE,
 630        TokenType.REPLACE,
 631        TokenType.RLIKE,
 632        TokenType.ROW,
 633        TokenType.UNNEST,
 634        TokenType.VAR,
 635        TokenType.LEFT,
 636        TokenType.RIGHT,
 637        TokenType.SEQUENCE,
 638        TokenType.DATE,
 639        TokenType.DATETIME,
 640        TokenType.TABLE,
 641        TokenType.TIMESTAMP,
 642        TokenType.TIMESTAMPTZ,
 643        TokenType.TRUNCATE,
 644        TokenType.WINDOW,
 645        TokenType.XOR,
 646        *TYPE_TOKENS,
 647        *SUBQUERY_PREDICATES,
 648    }
 649
 650    CONJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 651        TokenType.AND: exp.And,
 652    }
 653
 654    ASSIGNMENT: t.Dict[TokenType, t.Type[exp.Expression]] = {
 655        TokenType.COLON_EQ: exp.PropertyEQ,
 656    }
 657
 658    DISJUNCTION: t.Dict[TokenType, t.Type[exp.Expression]] = {
 659        TokenType.OR: exp.Or,
 660    }
 661
 662    EQUALITY = {
 663        TokenType.EQ: exp.EQ,
 664        TokenType.NEQ: exp.NEQ,
 665        TokenType.NULLSAFE_EQ: exp.NullSafeEQ,
 666    }
 667
 668    COMPARISON = {
 669        TokenType.GT: exp.GT,
 670        TokenType.GTE: exp.GTE,
 671        TokenType.LT: exp.LT,
 672        TokenType.LTE: exp.LTE,
 673    }
 674
 675    BITWISE = {
 676        TokenType.AMP: exp.BitwiseAnd,
 677        TokenType.CARET: exp.BitwiseXor,
 678        TokenType.PIPE: exp.BitwiseOr,
 679    }
 680
 681    TERM = {
 682        TokenType.DASH: exp.Sub,
 683        TokenType.PLUS: exp.Add,
 684        TokenType.MOD: exp.Mod,
 685        TokenType.COLLATE: exp.Collate,
 686    }
 687
 688    FACTOR = {
 689        TokenType.DIV: exp.IntDiv,
 690        TokenType.LR_ARROW: exp.Distance,
 691        TokenType.SLASH: exp.Div,
 692        TokenType.STAR: exp.Mul,
 693    }
 694
 695    EXPONENT: t.Dict[TokenType, t.Type[exp.Expression]] = {}
 696
 697    TIMES = {
 698        TokenType.TIME,
 699        TokenType.TIMETZ,
 700    }
 701
 702    TIMESTAMPS = {
 703        TokenType.TIMESTAMP,
 704        TokenType.TIMESTAMPNTZ,
 705        TokenType.TIMESTAMPTZ,
 706        TokenType.TIMESTAMPLTZ,
 707        *TIMES,
 708    }
 709
 710    SET_OPERATIONS = {
 711        TokenType.UNION,
 712        TokenType.INTERSECT,
 713        TokenType.EXCEPT,
 714    }
 715
 716    JOIN_METHODS = {
 717        TokenType.ASOF,
 718        TokenType.NATURAL,
 719        TokenType.POSITIONAL,
 720    }
 721
 722    JOIN_SIDES = {
 723        TokenType.LEFT,
 724        TokenType.RIGHT,
 725        TokenType.FULL,
 726    }
 727
 728    JOIN_KINDS = {
 729        TokenType.ANTI,
 730        TokenType.CROSS,
 731        TokenType.INNER,
 732        TokenType.OUTER,
 733        TokenType.SEMI,
 734        TokenType.STRAIGHT_JOIN,
 735    }
 736
 737    JOIN_HINTS: t.Set[str] = set()
 738
 739    LAMBDAS = {
 740        TokenType.ARROW: lambda self, expressions: self.expression(
 741            exp.Lambda,
 742            this=self._replace_lambda(
 743                self._parse_assignment(),
 744                expressions,
 745            ),
 746            expressions=expressions,
 747        ),
 748        TokenType.FARROW: lambda self, expressions: self.expression(
 749            exp.Kwarg,
 750            this=exp.var(expressions[0].name),
 751            expression=self._parse_assignment(),
 752        ),
 753    }
 754
 755    COLUMN_OPERATORS = {
 756        TokenType.DOT: None,
 757        TokenType.DOTCOLON: lambda self, this, to: self.expression(
 758            exp.JSONCast,
 759            this=this,
 760            to=to,
 761        ),
 762        TokenType.DCOLON: lambda self, this, to: self.expression(
 763            exp.Cast if self.STRICT_CAST else exp.TryCast,
 764            this=this,
 765            to=to,
 766        ),
 767        TokenType.ARROW: lambda self, this, path: self.expression(
 768            exp.JSONExtract,
 769            this=this,
 770            expression=self.dialect.to_json_path(path),
 771            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 772        ),
 773        TokenType.DARROW: lambda self, this, path: self.expression(
 774            exp.JSONExtractScalar,
 775            this=this,
 776            expression=self.dialect.to_json_path(path),
 777            only_json_types=self.JSON_ARROWS_REQUIRE_JSON_TYPE,
 778        ),
 779        TokenType.HASH_ARROW: lambda self, this, path: self.expression(
 780            exp.JSONBExtract,
 781            this=this,
 782            expression=path,
 783        ),
 784        TokenType.DHASH_ARROW: lambda self, this, path: self.expression(
 785            exp.JSONBExtractScalar,
 786            this=this,
 787            expression=path,
 788        ),
 789        TokenType.PLACEHOLDER: lambda self, this, key: self.expression(
 790            exp.JSONBContains,
 791            this=this,
 792            expression=key,
 793        ),
 794    }
 795
 796    EXPRESSION_PARSERS = {
 797        exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
 798        exp.Column: lambda self: self._parse_column(),
 799        exp.Condition: lambda self: self._parse_assignment(),
 800        exp.DataType: lambda self: self._parse_types(allow_identifiers=False, schema=True),
 801        exp.Expression: lambda self: self._parse_expression(),
 802        exp.From: lambda self: self._parse_from(joins=True),
 803        exp.Group: lambda self: self._parse_group(),
 804        exp.Having: lambda self: self._parse_having(),
 805        exp.Hint: lambda self: self._parse_hint_body(),
 806        exp.Identifier: lambda self: self._parse_id_var(),
 807        exp.Join: lambda self: self._parse_join(),
 808        exp.Lambda: lambda self: self._parse_lambda(),
 809        exp.Lateral: lambda self: self._parse_lateral(),
 810        exp.Limit: lambda self: self._parse_limit(),
 811        exp.Offset: lambda self: self._parse_offset(),
 812        exp.Order: lambda self: self._parse_order(),
 813        exp.Ordered: lambda self: self._parse_ordered(),
 814        exp.Properties: lambda self: self._parse_properties(),
 815        exp.PartitionedByProperty: lambda self: self._parse_partitioned_by(),
 816        exp.Qualify: lambda self: self._parse_qualify(),
 817        exp.Returning: lambda self: self._parse_returning(),
 818        exp.Select: lambda self: self._parse_select(),
 819        exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY),
 820        exp.Table: lambda self: self._parse_table_parts(),
 821        exp.TableAlias: lambda self: self._parse_table_alias(),
 822        exp.Tuple: lambda self: self._parse_value(values=False),
 823        exp.Whens: lambda self: self._parse_when_matched(),
 824        exp.Where: lambda self: self._parse_where(),
 825        exp.Window: lambda self: self._parse_named_window(),
 826        exp.With: lambda self: self._parse_with(),
 827        "JOIN_TYPE": lambda self: self._parse_join_parts(),
 828    }
 829
 830    STATEMENT_PARSERS = {
 831        TokenType.ALTER: lambda self: self._parse_alter(),
 832        TokenType.ANALYZE: lambda self: self._parse_analyze(),
 833        TokenType.BEGIN: lambda self: self._parse_transaction(),
 834        TokenType.CACHE: lambda self: self._parse_cache(),
 835        TokenType.COMMENT: lambda self: self._parse_comment(),
 836        TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(),
 837        TokenType.COPY: lambda self: self._parse_copy(),
 838        TokenType.CREATE: lambda self: self._parse_create(),
 839        TokenType.DELETE: lambda self: self._parse_delete(),
 840        TokenType.DESC: lambda self: self._parse_describe(),
 841        TokenType.DESCRIBE: lambda self: self._parse_describe(),
 842        TokenType.DROP: lambda self: self._parse_drop(),
 843        TokenType.GRANT: lambda self: self._parse_grant(),
 844        TokenType.INSERT: lambda self: self._parse_insert(),
 845        TokenType.KILL: lambda self: self._parse_kill(),
 846        TokenType.LOAD: lambda self: self._parse_load(),
 847        TokenType.MERGE: lambda self: self._parse_merge(),
 848        TokenType.PIVOT: lambda self: self._parse_simplified_pivot(),
 849        TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()),
 850        TokenType.REFRESH: lambda self: self._parse_refresh(),
 851        TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(),
 852        TokenType.SET: lambda self: self._parse_set(),
 853        TokenType.TRUNCATE: lambda self: self._parse_truncate_table(),
 854        TokenType.UNCACHE: lambda self: self._parse_uncache(),
 855        TokenType.UNPIVOT: lambda self: self._parse_simplified_pivot(is_unpivot=True),
 856        TokenType.UPDATE: lambda self: self._parse_update(),
 857        TokenType.USE: lambda self: self._parse_use(),
 858        TokenType.SEMICOLON: lambda self: exp.Semicolon(),
 859    }
 860
 861    UNARY_PARSERS = {
 862        TokenType.PLUS: lambda self: self._parse_unary(),  # Unary + is handled as a no-op
 863        TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()),
 864        TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()),
 865        TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()),
 866        TokenType.PIPE_SLASH: lambda self: self.expression(exp.Sqrt, this=self._parse_unary()),
 867        TokenType.DPIPE_SLASH: lambda self: self.expression(exp.Cbrt, this=self._parse_unary()),
 868    }
 869
 870    STRING_PARSERS = {
 871        TokenType.HEREDOC_STRING: lambda self, token: self.expression(
 872            exp.RawString, this=token.text
 873        ),
 874        TokenType.NATIONAL_STRING: lambda self, token: self.expression(
 875            exp.National, this=token.text
 876        ),
 877        TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text),
 878        TokenType.STRING: lambda self, token: self.expression(
 879            exp.Literal, this=token.text, is_string=True
 880        ),
 881        TokenType.UNICODE_STRING: lambda self, token: self.expression(
 882            exp.UnicodeString,
 883            this=token.text,
 884            escape=self._match_text_seq("UESCAPE") and self._parse_string(),
 885        ),
 886    }
 887
 888    NUMERIC_PARSERS = {
 889        TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text),
 890        TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text),
 891        TokenType.HEX_STRING: lambda self, token: self.expression(
 892            exp.HexString,
 893            this=token.text,
 894            is_integer=self.dialect.HEX_STRING_IS_INTEGER_TYPE or None,
 895        ),
 896        TokenType.NUMBER: lambda self, token: self.expression(
 897            exp.Literal, this=token.text, is_string=False
 898        ),
 899    }
 900
 901    PRIMARY_PARSERS = {
 902        **STRING_PARSERS,
 903        **NUMERIC_PARSERS,
 904        TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token),
 905        TokenType.NULL: lambda self, _: self.expression(exp.Null),
 906        TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True),
 907        TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False),
 908        TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(),
 909        TokenType.STAR: lambda self, _: self._parse_star_ops(),
 910    }
 911
 912    PLACEHOLDER_PARSERS = {
 913        TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
 914        TokenType.PARAMETER: lambda self: self._parse_parameter(),
 915        TokenType.COLON: lambda self: (
 916            self.expression(exp.Placeholder, this=self._prev.text)
 917            if self._match_set(self.COLON_PLACEHOLDER_TOKENS)
 918            else None
 919        ),
 920    }
 921
 922    RANGE_PARSERS = {
 923        TokenType.AT_GT: binary_range_parser(exp.ArrayContainsAll),
 924        TokenType.BETWEEN: lambda self, this: self._parse_between(this),
 925        TokenType.GLOB: binary_range_parser(exp.Glob),
 926        TokenType.ILIKE: binary_range_parser(exp.ILike),
 927        TokenType.IN: lambda self, this: self._parse_in(this),
 928        TokenType.IRLIKE: binary_range_parser(exp.RegexpILike),
 929        TokenType.IS: lambda self, this: self._parse_is(this),
 930        TokenType.LIKE: binary_range_parser(exp.Like),
 931        TokenType.LT_AT: binary_range_parser(exp.ArrayContainsAll, reverse_args=True),
 932        TokenType.OVERLAPS: binary_range_parser(exp.Overlaps),
 933        TokenType.RLIKE: binary_range_parser(exp.RegexpLike),
 934        TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo),
 935        TokenType.FOR: lambda self, this: self._parse_comprehension(this),
 936    }
 937
 938    PIPE_SYNTAX_TRANSFORM_PARSERS = {
 939        "AGGREGATE": lambda self, query: self._parse_pipe_syntax_aggregate(query),
 940        "AS": lambda self, query: self._build_pipe_cte(
 941            query, [exp.Star()], self._parse_table_alias()
 942        ),
 943        "EXTEND": lambda self, query: self._parse_pipe_syntax_extend(query),
 944        "LIMIT": lambda self, query: self._parse_pipe_syntax_limit(query),
 945        "ORDER BY": lambda self, query: query.order_by(
 946            self._parse_order(), append=False, copy=False
 947        ),
 948        "PIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
 949        "SELECT": lambda self, query: self._parse_pipe_syntax_select(query),
 950        "TABLESAMPLE": lambda self, query: self._parse_pipe_syntax_tablesample(query),
 951        "UNPIVOT": lambda self, query: self._parse_pipe_syntax_pivot(query),
 952        "WHERE": lambda self, query: query.where(self._parse_where(), copy=False),
 953    }
 954
 955    PROPERTY_PARSERS: t.Dict[str, t.Callable] = {
 956        "ALLOWED_VALUES": lambda self: self.expression(
 957            exp.AllowedValuesProperty, expressions=self._parse_csv(self._parse_primary)
 958        ),
 959        "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty),
 960        "AUTO": lambda self: self._parse_auto_property(),
 961        "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty),
 962        "BACKUP": lambda self: self.expression(
 963            exp.BackupProperty, this=self._parse_var(any_token=True)
 964        ),
 965        "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(),
 966        "CHARSET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 967        "CHARACTER SET": lambda self, **kwargs: self._parse_character_set(**kwargs),
 968        "CHECKSUM": lambda self: self._parse_checksum(),
 969        "CLUSTER BY": lambda self: self._parse_cluster(),
 970        "CLUSTERED": lambda self: self._parse_clustered_by(),
 971        "COLLATE": lambda self, **kwargs: self._parse_property_assignment(
 972            exp.CollateProperty, **kwargs
 973        ),
 974        "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty),
 975        "CONTAINS": lambda self: self._parse_contains_property(),
 976        "COPY": lambda self: self._parse_copy_property(),
 977        "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs),
 978        "DATA_DELETION": lambda self: self._parse_data_deletion_property(),
 979        "DEFINER": lambda self: self._parse_definer(),
 980        "DETERMINISTIC": lambda self: self.expression(
 981            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
 982        ),
 983        "DISTRIBUTED": lambda self: self._parse_distributed_property(),
 984        "DUPLICATE": lambda self: self._parse_composite_key_property(exp.DuplicateKeyProperty),
 985        "DYNAMIC": lambda self: self.expression(exp.DynamicProperty),
 986        "DISTKEY": lambda self: self._parse_distkey(),
 987        "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty),
 988        "EMPTY": lambda self: self.expression(exp.EmptyProperty),
 989        "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty),
 990        "ENVIRONMENT": lambda self: self.expression(
 991            exp.EnviromentProperty, expressions=self._parse_wrapped_csv(self._parse_assignment)
 992        ),
 993        "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty),
 994        "EXTERNAL": lambda self: self.expression(exp.ExternalProperty),
 995        "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs),
 996        "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
 997        "FREESPACE": lambda self: self._parse_freespace(),
 998        "GLOBAL": lambda self: self.expression(exp.GlobalProperty),
 999        "HEAP": lambda self: self.expression(exp.HeapProperty),
1000        "ICEBERG": lambda self: self.expression(exp.IcebergProperty),
1001        "IMMUTABLE": lambda self: self.expression(
1002            exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE")
1003        ),
1004        "INHERITS": lambda self: self.expression(
1005            exp.InheritsProperty, expressions=self._parse_wrapped_csv(self._parse_table)
1006        ),
1007        "INPUT": lambda self: self.expression(exp.InputModelProperty, this=self._parse_schema()),
1008        "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs),
1009        "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty),
1010        "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"),
1011        "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"),
1012        "LIKE": lambda self: self._parse_create_like(),
1013        "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty),
1014        "LOCK": lambda self: self._parse_locking(),
1015        "LOCKING": lambda self: self._parse_locking(),
1016        "LOG": lambda self, **kwargs: self._parse_log(**kwargs),
1017        "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty),
1018        "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs),
1019        "MODIFIES": lambda self: self._parse_modifies_property(),
1020        "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True),
1021        "NO": lambda self: self._parse_no_property(),
1022        "ON": lambda self: self._parse_on_property(),
1023        "ORDER BY": lambda self: self._parse_order(skip_order_token=True),
1024        "OUTPUT": lambda self: self.expression(exp.OutputModelProperty, this=self._parse_schema()),
1025        "PARTITION": lambda self: self._parse_partitioned_of(),
1026        "PARTITION BY": lambda self: self._parse_partitioned_by(),
1027        "PARTITIONED BY": lambda self: self._parse_partitioned_by(),
1028        "PARTITIONED_BY": lambda self: self._parse_partitioned_by(),
1029        "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True),
1030        "RANGE": lambda self: self._parse_dict_range(this="RANGE"),
1031        "READS": lambda self: self._parse_reads_property(),
1032        "REMOTE": lambda self: self._parse_remote_with_connection(),
1033        "RETURNS": lambda self: self._parse_returns(),
1034        "STRICT": lambda self: self.expression(exp.StrictProperty),
1035        "STREAMING": lambda self: self.expression(exp.StreamingTableProperty),
1036        "ROW": lambda self: self._parse_row(),
1037        "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty),
1038        "SAMPLE": lambda self: self.expression(
1039            exp.SampleProperty, this=self._match_text_seq("BY") and self._parse_bitwise()
1040        ),
1041        "SECURE": lambda self: self.expression(exp.SecureProperty),
1042        "SECURITY": lambda self: self._parse_security(),
1043        "SET": lambda self: self.expression(exp.SetProperty, multi=False),
1044        "SETTINGS": lambda self: self._parse_settings_property(),
1045        "SHARING": lambda self: self._parse_property_assignment(exp.SharingProperty),
1046        "SORTKEY": lambda self: self._parse_sortkey(),
1047        "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"),
1048        "STABLE": lambda self: self.expression(
1049            exp.StabilityProperty, this=exp.Literal.string("STABLE")
1050        ),
1051        "STORED": lambda self: self._parse_stored(),
1052        "SYSTEM_VERSIONING": lambda self: self._parse_system_versioning_property(),
1053        "TBLPROPERTIES": lambda self: self._parse_wrapped_properties(),
1054        "TEMP": lambda self: self.expression(exp.TemporaryProperty),
1055        "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty),
1056        "TO": lambda self: self._parse_to_table(),
1057        "TRANSIENT": lambda self: self.expression(exp.TransientProperty),
1058        "TRANSFORM": lambda self: self.expression(
1059            exp.TransformModelProperty, expressions=self._parse_wrapped_csv(self._parse_expression)
1060        ),
1061        "TTL": lambda self: self._parse_ttl(),
1062        "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty),
1063        "UNLOGGED": lambda self: self.expression(exp.UnloggedProperty),
1064        "VOLATILE": lambda self: self._parse_volatile_property(),
1065        "WITH": lambda self: self._parse_with_property(),
1066    }
1067
1068    CONSTRAINT_PARSERS = {
1069        "AUTOINCREMENT": lambda self: self._parse_auto_increment(),
1070        "AUTO_INCREMENT": lambda self: self._parse_auto_increment(),
1071        "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False),
1072        "CHARACTER SET": lambda self: self.expression(
1073            exp.CharacterSetColumnConstraint, this=self._parse_var_or_string()
1074        ),
1075        "CHECK": lambda self: self.expression(
1076            exp.CheckColumnConstraint,
1077            this=self._parse_wrapped(self._parse_assignment),
1078            enforced=self._match_text_seq("ENFORCED"),
1079        ),
1080        "COLLATE": lambda self: self.expression(
1081            exp.CollateColumnConstraint,
1082            this=self._parse_identifier() or self._parse_column(),
1083        ),
1084        "COMMENT": lambda self: self.expression(
1085            exp.CommentColumnConstraint, this=self._parse_string()
1086        ),
1087        "COMPRESS": lambda self: self._parse_compress(),
1088        "CLUSTERED": lambda self: self.expression(
1089            exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1090        ),
1091        "NONCLUSTERED": lambda self: self.expression(
1092            exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered)
1093        ),
1094        "DEFAULT": lambda self: self.expression(
1095            exp.DefaultColumnConstraint, this=self._parse_bitwise()
1096        ),
1097        "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()),
1098        "EPHEMERAL": lambda self: self.expression(
1099            exp.EphemeralColumnConstraint, this=self._parse_bitwise()
1100        ),
1101        "EXCLUDE": lambda self: self.expression(
1102            exp.ExcludeColumnConstraint, this=self._parse_index_params()
1103        ),
1104        "FOREIGN KEY": lambda self: self._parse_foreign_key(),
1105        "FORMAT": lambda self: self.expression(
1106            exp.DateFormatColumnConstraint, this=self._parse_var_or_string()
1107        ),
1108        "GENERATED": lambda self: self._parse_generated_as_identity(),
1109        "IDENTITY": lambda self: self._parse_auto_increment(),
1110        "INLINE": lambda self: self._parse_inline(),
1111        "LIKE": lambda self: self._parse_create_like(),
1112        "NOT": lambda self: self._parse_not_constraint(),
1113        "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True),
1114        "ON": lambda self: (
1115            self._match(TokenType.UPDATE)
1116            and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function())
1117        )
1118        or self.expression(exp.OnProperty, this=self._parse_id_var()),
1119        "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()),
1120        "PERIOD": lambda self: self._parse_period_for_system_time(),
1121        "PRIMARY KEY": lambda self: self._parse_primary_key(),
1122        "REFERENCES": lambda self: self._parse_references(match=False),
1123        "TITLE": lambda self: self.expression(
1124            exp.TitleColumnConstraint, this=self._parse_var_or_string()
1125        ),
1126        "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]),
1127        "UNIQUE": lambda self: self._parse_unique(),
1128        "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint),
1129        "WATERMARK": lambda self: self.expression(
1130            exp.WatermarkColumnConstraint,
1131            this=self._match(TokenType.FOR) and self._parse_column(),
1132            expression=self._match(TokenType.ALIAS) and self._parse_disjunction(),
1133        ),
1134        "WITH": lambda self: self.expression(
1135            exp.Properties, expressions=self._parse_wrapped_properties()
1136        ),
1137        "BUCKET": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1138        "TRUNCATE": lambda self: self._parse_partitioned_by_bucket_or_truncate(),
1139    }
1140
1141    def _parse_partitioned_by_bucket_or_truncate(self) -> exp.Expression:
1142        klass = (
1143            exp.PartitionedByBucket
1144            if self._prev.text.upper() == "BUCKET"
1145            else exp.PartitionByTruncate
1146        )
1147
1148        args = self._parse_wrapped_csv(lambda: self._parse_primary() or self._parse_column())
1149        this, expression = seq_get(args, 0), seq_get(args, 1)
1150
1151        if isinstance(this, exp.Literal):
1152            # Check for Iceberg partition transforms (bucket / truncate) and ensure their arguments are in the right order
1153            #  - For Hive, it's `bucket(<num buckets>, <col name>)` or `truncate(<num_chars>, <col_name>)`
1154            #  - For Trino, it's reversed - `bucket(<col name>, <num buckets>)` or `truncate(<col_name>, <num_chars>)`
1155            # Both variants are canonicalized in the latter i.e `bucket(<col name>, <num buckets>)`
1156            #
1157            # Hive ref: https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg-creating-tables.html#querying-iceberg-partitioning
1158            # Trino ref: https://docs.aws.amazon.com/athena/latest/ug/create-table-as.html#ctas-table-properties
1159            this, expression = expression, this
1160
1161        return self.expression(klass, this=this, expression=expression)
1162
1163    ALTER_PARSERS = {
1164        "ADD": lambda self: self._parse_alter_table_add(),
1165        "AS": lambda self: self._parse_select(),
1166        "ALTER": lambda self: self._parse_alter_table_alter(),
1167        "CLUSTER BY": lambda self: self._parse_cluster(wrapped=True),
1168        "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()),
1169        "DROP": lambda self: self._parse_alter_table_drop(),
1170        "RENAME": lambda self: self._parse_alter_table_rename(),
1171        "SET": lambda self: self._parse_alter_table_set(),
1172        "SWAP": lambda self: self.expression(
1173            exp.SwapTable, this=self._match(TokenType.WITH) and self._parse_table(schema=True)
1174        ),
1175    }
1176
1177    ALTER_ALTER_PARSERS = {
1178        "DISTKEY": lambda self: self._parse_alter_diststyle(),
1179        "DISTSTYLE": lambda self: self._parse_alter_diststyle(),
1180        "SORTKEY": lambda self: self._parse_alter_sortkey(),
1181        "COMPOUND": lambda self: self._parse_alter_sortkey(compound=True),
1182    }
1183
1184    SCHEMA_UNNAMED_CONSTRAINTS = {
1185        "CHECK",
1186        "EXCLUDE",
1187        "FOREIGN KEY",
1188        "LIKE",
1189        "PERIOD",
1190        "PRIMARY KEY",
1191        "UNIQUE",
1192        "WATERMARK",
1193        "BUCKET",
1194        "TRUNCATE",
1195    }
1196
1197    NO_PAREN_FUNCTION_PARSERS = {
1198        "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()),
1199        "CASE": lambda self: self._parse_case(),
1200        "CONNECT_BY_ROOT": lambda self: self.expression(
1201            exp.ConnectByRoot, this=self._parse_column()
1202        ),
1203        "IF": lambda self: self._parse_if(),
1204    }
1205
1206    INVALID_FUNC_NAME_TOKENS = {
1207        TokenType.IDENTIFIER,
1208        TokenType.STRING,
1209    }
1210
1211    FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
1212
1213    KEY_VALUE_DEFINITIONS = (exp.Alias, exp.EQ, exp.PropertyEQ, exp.Slice)
1214
1215    FUNCTION_PARSERS = {
1216        **{
1217            name: lambda self: self._parse_max_min_by(exp.ArgMax) for name in exp.ArgMax.sql_names()
1218        },
1219        **{
1220            name: lambda self: self._parse_max_min_by(exp.ArgMin) for name in exp.ArgMin.sql_names()
1221        },
1222        "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
1223        "CEIL": lambda self: self._parse_ceil_floor(exp.Ceil),
1224        "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
1225        "DECODE": lambda self: self._parse_decode(),
1226        "EXTRACT": lambda self: self._parse_extract(),
1227        "FLOOR": lambda self: self._parse_ceil_floor(exp.Floor),
1228        "GAP_FILL": lambda self: self._parse_gap_fill(),
1229        "JSON_OBJECT": lambda self: self._parse_json_object(),
1230        "JSON_OBJECTAGG": lambda self: self._parse_json_object(agg=True),
1231        "JSON_TABLE": lambda self: self._parse_json_table(),
1232        "MATCH": lambda self: self._parse_match_against(),
1233        "NORMALIZE": lambda self: self._parse_normalize(),
1234        "OPENJSON": lambda self: self._parse_open_json(),
1235        "OVERLAY": lambda self: self._parse_overlay(),
1236        "POSITION": lambda self: self._parse_position(),
1237        "PREDICT": lambda self: self._parse_predict(),
1238        "SAFE_CAST": lambda self: self._parse_cast(False, safe=True),
1239        "STRING_AGG": lambda self: self._parse_string_agg(),
1240        "SUBSTRING": lambda self: self._parse_substring(),
1241        "TRIM": lambda self: self._parse_trim(),
1242        "TRY_CAST": lambda self: self._parse_cast(False, safe=True),
1243        "TRY_CONVERT": lambda self: self._parse_convert(False, safe=True),
1244        "XMLELEMENT": lambda self: self.expression(
1245            exp.XMLElement,
1246            this=self._match_text_seq("NAME") and self._parse_id_var(),
1247            expressions=self._match(TokenType.COMMA) and self._parse_csv(self._parse_expression),
1248        ),
1249        "XMLTABLE": lambda self: self._parse_xml_table(),
1250    }
1251
1252    QUERY_MODIFIER_PARSERS = {
1253        TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()),
1254        TokenType.PREWHERE: lambda self: ("prewhere", self._parse_prewhere()),
1255        TokenType.WHERE: lambda self: ("where", self._parse_where()),
1256        TokenType.GROUP_BY: lambda self: ("group", self._parse_group()),
1257        TokenType.HAVING: lambda self: ("having", self._parse_having()),
1258        TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()),
1259        TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()),
1260        TokenType.ORDER_BY: lambda self: ("order", self._parse_order()),
1261        TokenType.LIMIT: lambda self: ("limit", self._parse_limit()),
1262        TokenType.FETCH: lambda self: ("limit", self._parse_limit()),
1263        TokenType.OFFSET: lambda self: ("offset", self._parse_offset()),
1264        TokenType.FOR: lambda self: ("locks", self._parse_locks()),
1265        TokenType.LOCK: lambda self: ("locks", self._parse_locks()),
1266        TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1267        TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)),
1268        TokenType.CLUSTER_BY: lambda self: (
1269            "cluster",
1270            self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY),
1271        ),
1272        TokenType.DISTRIBUTE_BY: lambda self: (
1273            "distribute",
1274            self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY),
1275        ),
1276        TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)),
1277        TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)),
1278        TokenType.START_WITH: lambda self: ("connect", self._parse_connect()),
1279    }
1280
1281    SET_PARSERS = {
1282        "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"),
1283        "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"),
1284        "SESSION": lambda self: self._parse_set_item_assignment("SESSION"),
1285        "TRANSACTION": lambda self: self._parse_set_transaction(),
1286    }
1287
1288    SHOW_PARSERS: t.Dict[str, t.Callable] = {}
1289
1290    TYPE_LITERAL_PARSERS = {
1291        exp.DataType.Type.JSON: lambda self, this, _: self.expression(exp.ParseJSON, this=this),
1292    }
1293
1294    TYPE_CONVERTERS: t.Dict[exp.DataType.Type, t.Callable[[exp.DataType], exp.DataType]] = {}
1295
1296    DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN}
1297
1298    PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE}
1299
1300    TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"}
1301    TRANSACTION_CHARACTERISTICS: OPTIONS_TYPE = {
1302        "ISOLATION": (
1303            ("LEVEL", "REPEATABLE", "READ"),
1304            ("LEVEL", "READ", "COMMITTED"),
1305            ("LEVEL", "READ", "UNCOMITTED"),
1306            ("LEVEL", "SERIALIZABLE"),
1307        ),
1308        "READ": ("WRITE", "ONLY"),
1309    }
1310
1311    CONFLICT_ACTIONS: OPTIONS_TYPE = dict.fromkeys(
1312        ("ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK", "UPDATE"), tuple()
1313    )
1314    CONFLICT_ACTIONS["DO"] = ("NOTHING", "UPDATE")
1315
1316    CREATE_SEQUENCE: OPTIONS_TYPE = {
1317        "SCALE": ("EXTEND", "NOEXTEND"),
1318        "SHARD": ("EXTEND", "NOEXTEND"),
1319        "NO": ("CYCLE", "CACHE", "MAXVALUE", "MINVALUE"),
1320        **dict.fromkeys(
1321            (
1322                "SESSION",
1323                "GLOBAL",
1324                "KEEP",
1325                "NOKEEP",
1326                "ORDER",
1327                "NOORDER",
1328                "NOCACHE",
1329                "CYCLE",
1330                "NOCYCLE",
1331                "NOMINVALUE",
1332                "NOMAXVALUE",
1333                "NOSCALE",
1334                "NOSHARD",
1335            ),
1336            tuple(),
1337        ),
1338    }
1339
1340    ISOLATED_LOADING_OPTIONS: OPTIONS_TYPE = {"FOR": ("ALL", "INSERT", "NONE")}
1341
1342    USABLES: OPTIONS_TYPE = dict.fromkeys(
1343        ("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA", "CATALOG"), tuple()
1344    )
1345
1346    CAST_ACTIONS: OPTIONS_TYPE = dict.fromkeys(("RENAME", "ADD"), ("FIELDS",))
1347
1348    SCHEMA_BINDING_OPTIONS: OPTIONS_TYPE = {
1349        "TYPE": ("EVOLUTION",),
1350        **dict.fromkeys(("BINDING", "COMPENSATION", "EVOLUTION"), tuple()),
1351    }
1352
1353    PROCEDURE_OPTIONS: OPTIONS_TYPE = {}
1354
1355    EXECUTE_AS_OPTIONS: OPTIONS_TYPE = dict.fromkeys(("CALLER", "SELF", "OWNER"), tuple())
1356
1357    KEY_CONSTRAINT_OPTIONS: OPTIONS_TYPE = {
1358        "NOT": ("ENFORCED",),
1359        "MATCH": (
1360            "FULL",
1361            "PARTIAL",
1362            "SIMPLE",
1363        ),
1364        "INITIALLY": ("DEFERRED", "IMMEDIATE"),
1365        "USING": (
1366            "BTREE",
1367            "HASH",
1368        ),
1369        **dict.fromkeys(("DEFERRABLE", "NORELY", "RELY"), tuple()),
1370    }
1371
1372    WINDOW_EXCLUDE_OPTIONS: OPTIONS_TYPE = {
1373        "NO": ("OTHERS",),
1374        "CURRENT": ("ROW",),
1375        **dict.fromkeys(("GROUP", "TIES"), tuple()),
1376    }
1377
1378    INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"}
1379
1380    CLONE_KEYWORDS = {"CLONE", "COPY"}
1381    HISTORICAL_DATA_PREFIX = {"AT", "BEFORE", "END"}
1382    HISTORICAL_DATA_KIND = {"OFFSET", "STATEMENT", "STREAM", "TIMESTAMP", "VERSION"}
1383
1384    OPCLASS_FOLLOW_KEYWORDS = {"ASC", "DESC", "NULLS", "WITH"}
1385
1386    OPTYPE_FOLLOW_TOKENS = {TokenType.COMMA, TokenType.R_PAREN}
1387
1388    TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE}
1389
1390    VIEW_ATTRIBUTES = {"ENCRYPTION", "SCHEMABINDING", "VIEW_METADATA"}
1391
1392    WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS}
1393    WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER}
1394    WINDOW_SIDES = {"FOLLOWING", "PRECEDING"}
1395
1396    JSON_KEY_VALUE_SEPARATOR_TOKENS = {TokenType.COLON, TokenType.COMMA, TokenType.IS}
1397
1398    FETCH_TOKENS = ID_VAR_TOKENS - {TokenType.ROW, TokenType.ROWS, TokenType.PERCENT}
1399
1400    ADD_CONSTRAINT_TOKENS = {
1401        TokenType.CONSTRAINT,
1402        TokenType.FOREIGN_KEY,
1403        TokenType.INDEX,
1404        TokenType.KEY,
1405        TokenType.PRIMARY_KEY,
1406        TokenType.UNIQUE,
1407    }
1408
1409    DISTINCT_TOKENS = {TokenType.DISTINCT}
1410
1411    NULL_TOKENS = {TokenType.NULL}
1412
1413    UNNEST_OFFSET_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - SET_OPERATIONS
1414
1415    SELECT_START_TOKENS = {TokenType.L_PAREN, TokenType.WITH, TokenType.SELECT}
1416
1417    COPY_INTO_VARLEN_OPTIONS = {"FILE_FORMAT", "COPY_OPTIONS", "FORMAT_OPTIONS", "CREDENTIAL"}
1418
1419    IS_JSON_PREDICATE_KIND = {"VALUE", "SCALAR", "ARRAY", "OBJECT"}
1420
1421    ODBC_DATETIME_LITERALS = {
1422        "d": exp.Date,
1423        "t": exp.Time,
1424        "ts": exp.Timestamp,
1425    }
1426
1427    ON_CONDITION_TOKENS = {"ERROR", "NULL", "TRUE", "FALSE", "EMPTY"}
1428
1429    PRIVILEGE_FOLLOW_TOKENS = {TokenType.ON, TokenType.COMMA, TokenType.L_PAREN}
1430
1431    # The style options for the DESCRIBE statement
1432    DESCRIBE_STYLES = {"ANALYZE", "EXTENDED", "FORMATTED", "HISTORY"}
1433
1434    # The style options for the ANALYZE statement
1435    ANALYZE_STYLES = {
1436        "BUFFER_USAGE_LIMIT",
1437        "FULL",
1438        "LOCAL",
1439        "NO_WRITE_TO_BINLOG",
1440        "SAMPLE",
1441        "SKIP_LOCKED",
1442        "VERBOSE",
1443    }
1444
1445    ANALYZE_EXPRESSION_PARSERS = {
1446        "ALL": lambda self: self._parse_analyze_columns(),
1447        "COMPUTE": lambda self: self._parse_analyze_statistics(),
1448        "DELETE": lambda self: self._parse_analyze_delete(),
1449        "DROP": lambda self: self._parse_analyze_histogram(),
1450        "ESTIMATE": lambda self: self._parse_analyze_statistics(),
1451        "LIST": lambda self: self._parse_analyze_list(),
1452        "PREDICATE": lambda self: self._parse_analyze_columns(),
1453        "UPDATE": lambda self: self._parse_analyze_histogram(),
1454        "VALIDATE": lambda self: self._parse_analyze_validate(),
1455    }
1456
1457    PARTITION_KEYWORDS = {"PARTITION", "SUBPARTITION"}
1458
1459    AMBIGUOUS_ALIAS_TOKENS = (TokenType.LIMIT, TokenType.OFFSET)
1460
1461    OPERATION_MODIFIERS: t.Set[str] = set()
1462
1463    RECURSIVE_CTE_SEARCH_KIND = {"BREADTH", "DEPTH", "CYCLE"}
1464
1465    MODIFIABLES = (exp.Query, exp.Table, exp.TableFromRows)
1466
1467    STRICT_CAST = True
1468
1469    PREFIXED_PIVOT_COLUMNS = False
1470    IDENTIFY_PIVOT_STRINGS = False
1471
1472    LOG_DEFAULTS_TO_LN = False
1473
1474    # Whether the table sample clause expects CSV syntax
1475    TABLESAMPLE_CSV = False
1476
1477    # The default method used for table sampling
1478    DEFAULT_SAMPLING_METHOD: t.Optional[str] = None
1479
1480    # Whether the SET command needs a delimiter (e.g. "=") for assignments
1481    SET_REQUIRES_ASSIGNMENT_DELIMITER = True
1482
1483    # Whether the TRIM function expects the characters to trim as its first argument
1484    TRIM_PATTERN_FIRST = False
1485
1486    # Whether string aliases are supported `SELECT COUNT(*) 'count'`
1487    STRING_ALIASES = False
1488
1489    # Whether query modifiers such as LIMIT are attached to the UNION node (vs its right operand)
1490    MODIFIERS_ATTACHED_TO_SET_OP = True
1491    SET_OP_MODIFIERS = {"order", "limit", "offset"}
1492
1493    # Whether to parse IF statements that aren't followed by a left parenthesis as commands
1494    NO_PAREN_IF_COMMANDS = True
1495
1496    # Whether the -> and ->> operators expect documents of type JSON (e.g. Postgres)
1497    JSON_ARROWS_REQUIRE_JSON_TYPE = False
1498
1499    # Whether the `:` operator is used to extract a value from a VARIANT column
1500    COLON_IS_VARIANT_EXTRACT = False
1501
1502    # Whether or not a VALUES keyword needs to be followed by '(' to form a VALUES clause.
1503    # If this is True and '(' is not found, the keyword will be treated as an identifier
1504    VALUES_FOLLOWED_BY_PAREN = True
1505
1506    # Whether implicit unnesting is supported, e.g. SELECT 1 FROM y.z AS z, z.a (Redshift)
1507    SUPPORTS_IMPLICIT_UNNEST = False
1508
1509    # Whether or not interval spans are supported, INTERVAL 1 YEAR TO MONTHS
1510    INTERVAL_SPANS = True
1511
1512    # Whether a PARTITION clause can follow a table reference
1513    SUPPORTS_PARTITION_SELECTION = False
1514
1515    # Whether the `name AS expr` schema/column constraint requires parentheses around `expr`
1516    WRAPPED_TRANSFORM_COLUMN_CONSTRAINT = True
1517
1518    # Whether the 'AS' keyword is optional in the CTE definition syntax
1519    OPTIONAL_ALIAS_TOKEN_CTE = True
1520
1521    # Whether renaming a column with an ALTER statement requires the presence of the COLUMN keyword
1522    ALTER_RENAME_REQUIRES_COLUMN = True
1523
1524    # Whether all join types have the same precedence, i.e., they "naturally" produce a left-deep tree.
1525    # In standard SQL, joins that use the JOIN keyword take higher precedence than comma-joins. That is
1526    # to say, JOIN operators happen before comma operators. This is not the case in some dialects, such
1527    # as BigQuery, where all joins have the same precedence.
1528    JOINS_HAVE_EQUAL_PRECEDENCE = False
1529
1530    # Whether TIMESTAMP <literal> can produce a zone-aware timestamp
1531    ZONE_AWARE_TIMESTAMP_CONSTRUCTOR = False
1532
1533    __slots__ = (
1534        "error_level",
1535        "error_message_context",
1536        "max_errors",
1537        "dialect",
1538        "sql",
1539        "errors",
1540        "_tokens",
1541        "_index",
1542        "_curr",
1543        "_next",
1544        "_prev",
1545        "_prev_comments",
1546        "_pipe_cte_counter",
1547    )
1548
1549    # Autofilled
1550    SHOW_TRIE: t.Dict = {}
1551    SET_TRIE: t.Dict = {}
1552
1553    def __init__(
1554        self,
1555        error_level: t.Optional[ErrorLevel] = None,
1556        error_message_context: int = 100,
1557        max_errors: int = 3,
1558        dialect: DialectType = None,
1559    ):
1560        from sqlglot.dialects import Dialect
1561
1562        self.error_level = error_level or ErrorLevel.IMMEDIATE
1563        self.error_message_context = error_message_context
1564        self.max_errors = max_errors
1565        self.dialect = Dialect.get_or_raise(dialect)
1566        self.reset()
1567
1568    def reset(self):
1569        self.sql = ""
1570        self.errors = []
1571        self._tokens = []
1572        self._index = 0
1573        self._curr = None
1574        self._next = None
1575        self._prev = None
1576        self._prev_comments = None
1577        self._pipe_cte_counter = 0
1578
1579    def parse(
1580        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1581    ) -> t.List[t.Optional[exp.Expression]]:
1582        """
1583        Parses a list of tokens and returns a list of syntax trees, one tree
1584        per parsed SQL statement.
1585
1586        Args:
1587            raw_tokens: The list of tokens.
1588            sql: The original SQL string, used to produce helpful debug messages.
1589
1590        Returns:
1591            The list of the produced syntax trees.
1592        """
1593        return self._parse(
1594            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1595        )
1596
1597    def parse_into(
1598        self,
1599        expression_types: exp.IntoType,
1600        raw_tokens: t.List[Token],
1601        sql: t.Optional[str] = None,
1602    ) -> t.List[t.Optional[exp.Expression]]:
1603        """
1604        Parses a list of tokens into a given Expression type. If a collection of Expression
1605        types is given instead, this method will try to parse the token list into each one
1606        of them, stopping at the first for which the parsing succeeds.
1607
1608        Args:
1609            expression_types: The expression type(s) to try and parse the token list into.
1610            raw_tokens: The list of tokens.
1611            sql: The original SQL string, used to produce helpful debug messages.
1612
1613        Returns:
1614            The target Expression.
1615        """
1616        errors = []
1617        for expression_type in ensure_list(expression_types):
1618            parser = self.EXPRESSION_PARSERS.get(expression_type)
1619            if not parser:
1620                raise TypeError(f"No parser registered for {expression_type}")
1621
1622            try:
1623                return self._parse(parser, raw_tokens, sql)
1624            except ParseError as e:
1625                e.errors[0]["into_expression"] = expression_type
1626                errors.append(e)
1627
1628        raise ParseError(
1629            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1630            errors=merge_errors(errors),
1631        ) from errors[-1]
1632
1633    def _parse(
1634        self,
1635        parse_method: t.Callable[[Parser], t.Optional[exp.Expression]],
1636        raw_tokens: t.List[Token],
1637        sql: t.Optional[str] = None,
1638    ) -> t.List[t.Optional[exp.Expression]]:
1639        self.reset()
1640        self.sql = sql or ""
1641
1642        total = len(raw_tokens)
1643        chunks: t.List[t.List[Token]] = [[]]
1644
1645        for i, token in enumerate(raw_tokens):
1646            if token.token_type == TokenType.SEMICOLON:
1647                if token.comments:
1648                    chunks.append([token])
1649
1650                if i < total - 1:
1651                    chunks.append([])
1652            else:
1653                chunks[-1].append(token)
1654
1655        expressions = []
1656
1657        for tokens in chunks:
1658            self._index = -1
1659            self._tokens = tokens
1660            self._advance()
1661
1662            expressions.append(parse_method(self))
1663
1664            if self._index < len(self._tokens):
1665                self.raise_error("Invalid expression / Unexpected token")
1666
1667            self.check_errors()
1668
1669        return expressions
1670
1671    def check_errors(self) -> None:
1672        """Logs or raises any found errors, depending on the chosen error level setting."""
1673        if self.error_level == ErrorLevel.WARN:
1674            for error in self.errors:
1675                logger.error(str(error))
1676        elif self.error_level == ErrorLevel.RAISE and self.errors:
1677            raise ParseError(
1678                concat_messages(self.errors, self.max_errors),
1679                errors=merge_errors(self.errors),
1680            )
1681
1682    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1683        """
1684        Appends an error in the list of recorded errors or raises it, depending on the chosen
1685        error level setting.
1686        """
1687        token = token or self._curr or self._prev or Token.string("")
1688        start = token.start
1689        end = token.end + 1
1690        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1691        highlight = self.sql[start:end]
1692        end_context = self.sql[end : end + self.error_message_context]
1693
1694        error = ParseError.new(
1695            f"{message}. Line {token.line}, Col: {token.col}.\n"
1696            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1697            description=message,
1698            line=token.line,
1699            col=token.col,
1700            start_context=start_context,
1701            highlight=highlight,
1702            end_context=end_context,
1703        )
1704
1705        if self.error_level == ErrorLevel.IMMEDIATE:
1706            raise error
1707
1708        self.errors.append(error)
1709
1710    def expression(
1711        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1712    ) -> E:
1713        """
1714        Creates a new, validated Expression.
1715
1716        Args:
1717            exp_class: The expression class to instantiate.
1718            comments: An optional list of comments to attach to the expression.
1719            kwargs: The arguments to set for the expression along with their respective values.
1720
1721        Returns:
1722            The target expression.
1723        """
1724        instance = exp_class(**kwargs)
1725        instance.add_comments(comments) if comments else self._add_comments(instance)
1726        return self.validate_expression(instance)
1727
1728    def _add_comments(self, expression: t.Optional[exp.Expression]) -> None:
1729        if expression and self._prev_comments:
1730            expression.add_comments(self._prev_comments)
1731            self._prev_comments = None
1732
1733    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1734        """
1735        Validates an Expression, making sure that all its mandatory arguments are set.
1736
1737        Args:
1738            expression: The expression to validate.
1739            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1740
1741        Returns:
1742            The validated expression.
1743        """
1744        if self.error_level != ErrorLevel.IGNORE:
1745            for error_message in expression.error_messages(args):
1746                self.raise_error(error_message)
1747
1748        return expression
1749
1750    def _find_sql(self, start: Token, end: Token) -> str:
1751        return self.sql[start.start : end.end + 1]
1752
1753    def _is_connected(self) -> bool:
1754        return self._prev and self._curr and self._prev.end + 1 == self._curr.start
1755
1756    def _advance(self, times: int = 1) -> None:
1757        self._index += times
1758        self._curr = seq_get(self._tokens, self._index)
1759        self._next = seq_get(self._tokens, self._index + 1)
1760
1761        if self._index > 0:
1762            self._prev = self._tokens[self._index - 1]
1763            self._prev_comments = self._prev.comments
1764        else:
1765            self._prev = None
1766            self._prev_comments = None
1767
1768    def _retreat(self, index: int) -> None:
1769        if index != self._index:
1770            self._advance(index - self._index)
1771
1772    def _warn_unsupported(self) -> None:
1773        if len(self._tokens) <= 1:
1774            return
1775
1776        # We use _find_sql because self.sql may comprise multiple chunks, and we're only
1777        # interested in emitting a warning for the one being currently processed.
1778        sql = self._find_sql(self._tokens[0], self._tokens[-1])[: self.error_message_context]
1779
1780        logger.warning(
1781            f"'{sql}' contains unsupported syntax. Falling back to parsing as a 'Command'."
1782        )
1783
1784    def _parse_command(self) -> exp.Command:
1785        self._warn_unsupported()
1786        return self.expression(
1787            exp.Command,
1788            comments=self._prev_comments,
1789            this=self._prev.text.upper(),
1790            expression=self._parse_string(),
1791        )
1792
1793    def _try_parse(self, parse_method: t.Callable[[], T], retreat: bool = False) -> t.Optional[T]:
1794        """
1795        Attemps to backtrack if a parse function that contains a try/catch internally raises an error.
1796        This behavior can be different depending on the uset-set ErrorLevel, so _try_parse aims to
1797        solve this by setting & resetting the parser state accordingly
1798        """
1799        index = self._index
1800        error_level = self.error_level
1801
1802        self.error_level = ErrorLevel.IMMEDIATE
1803        try:
1804            this = parse_method()
1805        except ParseError:
1806            this = None
1807        finally:
1808            if not this or retreat:
1809                self._retreat(index)
1810            self.error_level = error_level
1811
1812        return this
1813
1814    def _parse_comment(self, allow_exists: bool = True) -> exp.Expression:
1815        start = self._prev
1816        exists = self._parse_exists() if allow_exists else None
1817
1818        self._match(TokenType.ON)
1819
1820        materialized = self._match_text_seq("MATERIALIZED")
1821        kind = self._match_set(self.CREATABLES) and self._prev
1822        if not kind:
1823            return self._parse_as_command(start)
1824
1825        if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
1826            this = self._parse_user_defined_function(kind=kind.token_type)
1827        elif kind.token_type == TokenType.TABLE:
1828            this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS)
1829        elif kind.token_type == TokenType.COLUMN:
1830            this = self._parse_column()
1831        else:
1832            this = self._parse_id_var()
1833
1834        self._match(TokenType.IS)
1835
1836        return self.expression(
1837            exp.Comment,
1838            this=this,
1839            kind=kind.text,
1840            expression=self._parse_string(),
1841            exists=exists,
1842            materialized=materialized,
1843        )
1844
1845    def _parse_to_table(
1846        self,
1847    ) -> exp.ToTableProperty:
1848        table = self._parse_table_parts(schema=True)
1849        return self.expression(exp.ToTableProperty, this=table)
1850
1851    # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl
1852    def _parse_ttl(self) -> exp.Expression:
1853        def _parse_ttl_action() -> t.Optional[exp.Expression]:
1854            this = self._parse_bitwise()
1855
1856            if self._match_text_seq("DELETE"):
1857                return self.expression(exp.MergeTreeTTLAction, this=this, delete=True)
1858            if self._match_text_seq("RECOMPRESS"):
1859                return self.expression(
1860                    exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise()
1861                )
1862            if self._match_text_seq("TO", "DISK"):
1863                return self.expression(
1864                    exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string()
1865                )
1866            if self._match_text_seq("TO", "VOLUME"):
1867                return self.expression(
1868                    exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string()
1869                )
1870
1871            return this
1872
1873        expressions = self._parse_csv(_parse_ttl_action)
1874        where = self._parse_where()
1875        group = self._parse_group()
1876
1877        aggregates = None
1878        if group and self._match(TokenType.SET):
1879            aggregates = self._parse_csv(self._parse_set_item)
1880
1881        return self.expression(
1882            exp.MergeTreeTTL,
1883            expressions=expressions,
1884            where=where,
1885            group=group,
1886            aggregates=aggregates,
1887        )
1888
1889    def _parse_statement(self) -> t.Optional[exp.Expression]:
1890        if self._curr is None:
1891            return None
1892
1893        if self._match_set(self.STATEMENT_PARSERS):
1894            comments = self._prev_comments
1895            stmt = self.STATEMENT_PARSERS[self._prev.token_type](self)
1896            stmt.add_comments(comments, prepend=True)
1897            return stmt
1898
1899        if self._match_set(self.dialect.tokenizer_class.COMMANDS):
1900            return self._parse_command()
1901
1902        expression = self._parse_expression()
1903        expression = self._parse_set_operations(expression) if expression else self._parse_select()
1904        return self._parse_query_modifiers(expression)
1905
1906    def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command:
1907        start = self._prev
1908        temporary = self._match(TokenType.TEMPORARY)
1909        materialized = self._match_text_seq("MATERIALIZED")
1910
1911        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
1912        if not kind:
1913            return self._parse_as_command(start)
1914
1915        concurrently = self._match_text_seq("CONCURRENTLY")
1916        if_exists = exists or self._parse_exists()
1917
1918        if kind == "COLUMN":
1919            this = self._parse_column()
1920        else:
1921            this = self._parse_table_parts(
1922                schema=True, is_db_reference=self._prev.token_type == TokenType.SCHEMA
1923            )
1924
1925        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
1926
1927        if self._match(TokenType.L_PAREN, advance=False):
1928            expressions = self._parse_wrapped_csv(self._parse_types)
1929        else:
1930            expressions = None
1931
1932        return self.expression(
1933            exp.Drop,
1934            exists=if_exists,
1935            this=this,
1936            expressions=expressions,
1937            kind=self.dialect.CREATABLE_KIND_MAPPING.get(kind) or kind,
1938            temporary=temporary,
1939            materialized=materialized,
1940            cascade=self._match_text_seq("CASCADE"),
1941            constraints=self._match_text_seq("CONSTRAINTS"),
1942            purge=self._match_text_seq("PURGE"),
1943            cluster=cluster,
1944            concurrently=concurrently,
1945        )
1946
1947    def _parse_exists(self, not_: bool = False) -> t.Optional[bool]:
1948        return (
1949            self._match_text_seq("IF")
1950            and (not not_ or self._match(TokenType.NOT))
1951            and self._match(TokenType.EXISTS)
1952        )
1953
1954    def _parse_create(self) -> exp.Create | exp.Command:
1955        # Note: this can't be None because we've matched a statement parser
1956        start = self._prev
1957
1958        replace = (
1959            start.token_type == TokenType.REPLACE
1960            or self._match_pair(TokenType.OR, TokenType.REPLACE)
1961            or self._match_pair(TokenType.OR, TokenType.ALTER)
1962        )
1963        refresh = self._match_pair(TokenType.OR, TokenType.REFRESH)
1964
1965        unique = self._match(TokenType.UNIQUE)
1966
1967        if self._match_text_seq("CLUSTERED", "COLUMNSTORE"):
1968            clustered = True
1969        elif self._match_text_seq("NONCLUSTERED", "COLUMNSTORE") or self._match_text_seq(
1970            "COLUMNSTORE"
1971        ):
1972            clustered = False
1973        else:
1974            clustered = None
1975
1976        if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False):
1977            self._advance()
1978
1979        properties = None
1980        create_token = self._match_set(self.CREATABLES) and self._prev
1981
1982        if not create_token:
1983            # exp.Properties.Location.POST_CREATE
1984            properties = self._parse_properties()
1985            create_token = self._match_set(self.CREATABLES) and self._prev
1986
1987            if not properties or not create_token:
1988                return self._parse_as_command(start)
1989
1990        concurrently = self._match_text_seq("CONCURRENTLY")
1991        exists = self._parse_exists(not_=True)
1992        this = None
1993        expression: t.Optional[exp.Expression] = None
1994        indexes = None
1995        no_schema_binding = None
1996        begin = None
1997        end = None
1998        clone = None
1999
2000        def extend_props(temp_props: t.Optional[exp.Properties]) -> None:
2001            nonlocal properties
2002            if properties and temp_props:
2003                properties.expressions.extend(temp_props.expressions)
2004            elif temp_props:
2005                properties = temp_props
2006
2007        if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE):
2008            this = self._parse_user_defined_function(kind=create_token.token_type)
2009
2010            # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature)
2011            extend_props(self._parse_properties())
2012
2013            expression = self._match(TokenType.ALIAS) and self._parse_heredoc()
2014            extend_props(self._parse_properties())
2015
2016            if not expression:
2017                if self._match(TokenType.COMMAND):
2018                    expression = self._parse_as_command(self._prev)
2019                else:
2020                    begin = self._match(TokenType.BEGIN)
2021                    return_ = self._match_text_seq("RETURN")
2022
2023                    if self._match(TokenType.STRING, advance=False):
2024                        # Takes care of BigQuery's JavaScript UDF definitions that end in an OPTIONS property
2025                        # # https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#create_function_statement
2026                        expression = self._parse_string()
2027                        extend_props(self._parse_properties())
2028                    else:
2029                        expression = self._parse_user_defined_function_expression()
2030
2031                    end = self._match_text_seq("END")
2032
2033                    if return_:
2034                        expression = self.expression(exp.Return, this=expression)
2035        elif create_token.token_type == TokenType.INDEX:
2036            # Postgres allows anonymous indexes, eg. CREATE INDEX IF NOT EXISTS ON t(c)
2037            if not self._match(TokenType.ON):
2038                index = self._parse_id_var()
2039                anonymous = False
2040            else:
2041                index = None
2042                anonymous = True
2043
2044            this = self._parse_index(index=index, anonymous=anonymous)
2045        elif create_token.token_type in self.DB_CREATABLES:
2046            table_parts = self._parse_table_parts(
2047                schema=True, is_db_reference=create_token.token_type == TokenType.SCHEMA
2048            )
2049
2050            # exp.Properties.Location.POST_NAME
2051            self._match(TokenType.COMMA)
2052            extend_props(self._parse_properties(before=True))
2053
2054            this = self._parse_schema(this=table_parts)
2055
2056            # exp.Properties.Location.POST_SCHEMA and POST_WITH
2057            extend_props(self._parse_properties())
2058
2059            has_alias = self._match(TokenType.ALIAS)
2060            if not self._match_set(self.DDL_SELECT_TOKENS, advance=False):
2061                # exp.Properties.Location.POST_ALIAS
2062                extend_props(self._parse_properties())
2063
2064            if create_token.token_type == TokenType.SEQUENCE:
2065                expression = self._parse_types()
2066                extend_props(self._parse_properties())
2067            else:
2068                expression = self._parse_ddl_select()
2069
2070                # Some dialects also support using a table as an alias instead of a SELECT.
2071                # Here we fallback to this as an alternative.
2072                if not expression and has_alias:
2073                    expression = self._try_parse(self._parse_table_parts)
2074
2075            if create_token.token_type == TokenType.TABLE:
2076                # exp.Properties.Location.POST_EXPRESSION
2077                extend_props(self._parse_properties())
2078
2079                indexes = []
2080                while True:
2081                    index = self._parse_index()
2082
2083                    # exp.Properties.Location.POST_INDEX
2084                    extend_props(self._parse_properties())
2085                    if not index:
2086                        break
2087                    else:
2088                        self._match(TokenType.COMMA)
2089                        indexes.append(index)
2090            elif create_token.token_type == TokenType.VIEW:
2091                if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"):
2092                    no_schema_binding = True
2093            elif create_token.token_type in (TokenType.SINK, TokenType.SOURCE):
2094                extend_props(self._parse_properties())
2095
2096            shallow = self._match_text_seq("SHALLOW")
2097
2098            if self._match_texts(self.CLONE_KEYWORDS):
2099                copy = self._prev.text.lower() == "copy"
2100                clone = self.expression(
2101                    exp.Clone, this=self._parse_table(schema=True), shallow=shallow, copy=copy
2102                )
2103
2104        if self._curr and not self._match_set((TokenType.R_PAREN, TokenType.COMMA), advance=False):
2105            return self._parse_as_command(start)
2106
2107        create_kind_text = create_token.text.upper()
2108        return self.expression(
2109            exp.Create,
2110            this=this,
2111            kind=self.dialect.CREATABLE_KIND_MAPPING.get(create_kind_text) or create_kind_text,
2112            replace=replace,
2113            refresh=refresh,
2114            unique=unique,
2115            expression=expression,
2116            exists=exists,
2117            properties=properties,
2118            indexes=indexes,
2119            no_schema_binding=no_schema_binding,
2120            begin=begin,
2121            end=end,
2122            clone=clone,
2123            concurrently=concurrently,
2124            clustered=clustered,
2125        )
2126
2127    def _parse_sequence_properties(self) -> t.Optional[exp.SequenceProperties]:
2128        seq = exp.SequenceProperties()
2129
2130        options = []
2131        index = self._index
2132
2133        while self._curr:
2134            self._match(TokenType.COMMA)
2135            if self._match_text_seq("INCREMENT"):
2136                self._match_text_seq("BY")
2137                self._match_text_seq("=")
2138                seq.set("increment", self._parse_term())
2139            elif self._match_text_seq("MINVALUE"):
2140                seq.set("minvalue", self._parse_term())
2141            elif self._match_text_seq("MAXVALUE"):
2142                seq.set("maxvalue", self._parse_term())
2143            elif self._match(TokenType.START_WITH) or self._match_text_seq("START"):
2144                self._match_text_seq("=")
2145                seq.set("start", self._parse_term())
2146            elif self._match_text_seq("CACHE"):
2147                # T-SQL allows empty CACHE which is initialized dynamically
2148                seq.set("cache", self._parse_number() or True)
2149            elif self._match_text_seq("OWNED", "BY"):
2150                # "OWNED BY NONE" is the default
2151                seq.set("owned", None if self._match_text_seq("NONE") else self._parse_column())
2152            else:
2153                opt = self._parse_var_from_options(self.CREATE_SEQUENCE, raise_unmatched=False)
2154                if opt:
2155                    options.append(opt)
2156                else:
2157                    break
2158
2159        seq.set("options", options if options else None)
2160        return None if self._index == index else seq
2161
2162    def _parse_property_before(self) -> t.Optional[exp.Expression]:
2163        # only used for teradata currently
2164        self._match(TokenType.COMMA)
2165
2166        kwargs = {
2167            "no": self._match_text_seq("NO"),
2168            "dual": self._match_text_seq("DUAL"),
2169            "before": self._match_text_seq("BEFORE"),
2170            "default": self._match_text_seq("DEFAULT"),
2171            "local": (self._match_text_seq("LOCAL") and "LOCAL")
2172            or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"),
2173            "after": self._match_text_seq("AFTER"),
2174            "minimum": self._match_texts(("MIN", "MINIMUM")),
2175            "maximum": self._match_texts(("MAX", "MAXIMUM")),
2176        }
2177
2178        if self._match_texts(self.PROPERTY_PARSERS):
2179            parser = self.PROPERTY_PARSERS[self._prev.text.upper()]
2180            try:
2181                return parser(self, **{k: v for k, v in kwargs.items() if v})
2182            except TypeError:
2183                self.raise_error(f"Cannot parse property '{self._prev.text}'")
2184
2185        return None
2186
2187    def _parse_wrapped_properties(self) -> t.List[exp.Expression]:
2188        return self._parse_wrapped_csv(self._parse_property)
2189
2190    def _parse_property(self) -> t.Optional[exp.Expression]:
2191        if self._match_texts(self.PROPERTY_PARSERS):
2192            return self.PROPERTY_PARSERS[self._prev.text.upper()](self)
2193
2194        if self._match(TokenType.DEFAULT) and self._match_texts(self.PROPERTY_PARSERS):
2195            return self.PROPERTY_PARSERS[self._prev.text.upper()](self, default=True)
2196
2197        if self._match_text_seq("COMPOUND", "SORTKEY"):
2198            return self._parse_sortkey(compound=True)
2199
2200        if self._match_text_seq("SQL", "SECURITY"):
2201            return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
2202
2203        index = self._index
2204        key = self._parse_column()
2205
2206        if not self._match(TokenType.EQ):
2207            self._retreat(index)
2208            return self._parse_sequence_properties()
2209
2210        # Transform the key to exp.Dot if it's dotted identifiers wrapped in exp.Column or to exp.Var otherwise
2211        if isinstance(key, exp.Column):
2212            key = key.to_dot() if len(key.parts) > 1 else exp.var(key.name)
2213
2214        value = self._parse_bitwise() or self._parse_var(any_token=True)
2215
2216        # Transform the value to exp.Var if it was parsed as exp.Column(exp.Identifier())
2217        if isinstance(value, exp.Column):
2218            value = exp.var(value.name)
2219
2220        return self.expression(exp.Property, this=key, value=value)
2221
2222    def _parse_stored(self) -> t.Union[exp.FileFormatProperty, exp.StorageHandlerProperty]:
2223        if self._match_text_seq("BY"):
2224            return self.expression(exp.StorageHandlerProperty, this=self._parse_var_or_string())
2225
2226        self._match(TokenType.ALIAS)
2227        input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None
2228        output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None
2229
2230        return self.expression(
2231            exp.FileFormatProperty,
2232            this=(
2233                self.expression(
2234                    exp.InputOutputFormat,
2235                    input_format=input_format,
2236                    output_format=output_format,
2237                )
2238                if input_format or output_format
2239                else self._parse_var_or_string() or self._parse_number() or self._parse_id_var()
2240            ),
2241        )
2242
2243    def _parse_unquoted_field(self) -> t.Optional[exp.Expression]:
2244        field = self._parse_field()
2245        if isinstance(field, exp.Identifier) and not field.quoted:
2246            field = exp.var(field)
2247
2248        return field
2249
2250    def _parse_property_assignment(self, exp_class: t.Type[E], **kwargs: t.Any) -> E:
2251        self._match(TokenType.EQ)
2252        self._match(TokenType.ALIAS)
2253
2254        return self.expression(exp_class, this=self._parse_unquoted_field(), **kwargs)
2255
2256    def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]:
2257        properties = []
2258        while True:
2259            if before:
2260                prop = self._parse_property_before()
2261            else:
2262                prop = self._parse_property()
2263            if not prop:
2264                break
2265            for p in ensure_list(prop):
2266                properties.append(p)
2267
2268        if properties:
2269            return self.expression(exp.Properties, expressions=properties)
2270
2271        return None
2272
2273    def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty:
2274        return self.expression(
2275            exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION")
2276        )
2277
2278    def _parse_security(self) -> t.Optional[exp.SecurityProperty]:
2279        if self._match_texts(("NONE", "DEFINER", "INVOKER")):
2280            security_specifier = self._prev.text.upper()
2281            return self.expression(exp.SecurityProperty, this=security_specifier)
2282        return None
2283
2284    def _parse_settings_property(self) -> exp.SettingsProperty:
2285        return self.expression(
2286            exp.SettingsProperty, expressions=self._parse_csv(self._parse_assignment)
2287        )
2288
2289    def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty:
2290        if self._index >= 2:
2291            pre_volatile_token = self._tokens[self._index - 2]
2292        else:
2293            pre_volatile_token = None
2294
2295        if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS:
2296            return exp.VolatileProperty()
2297
2298        return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE"))
2299
2300    def _parse_retention_period(self) -> exp.Var:
2301        # Parse TSQL's HISTORY_RETENTION_PERIOD: {INFINITE | <number> DAY | DAYS | MONTH ...}
2302        number = self._parse_number()
2303        number_str = f"{number} " if number else ""
2304        unit = self._parse_var(any_token=True)
2305        return exp.var(f"{number_str}{unit}")
2306
2307    def _parse_system_versioning_property(
2308        self, with_: bool = False
2309    ) -> exp.WithSystemVersioningProperty:
2310        self._match(TokenType.EQ)
2311        prop = self.expression(
2312            exp.WithSystemVersioningProperty,
2313            **{  # type: ignore
2314                "on": True,
2315                "with": with_,
2316            },
2317        )
2318
2319        if self._match_text_seq("OFF"):
2320            prop.set("on", False)
2321            return prop
2322
2323        self._match(TokenType.ON)
2324        if self._match(TokenType.L_PAREN):
2325            while self._curr and not self._match(TokenType.R_PAREN):
2326                if self._match_text_seq("HISTORY_TABLE", "="):
2327                    prop.set("this", self._parse_table_parts())
2328                elif self._match_text_seq("DATA_CONSISTENCY_CHECK", "="):
2329                    prop.set("data_consistency", self._advance_any() and self._prev.text.upper())
2330                elif self._match_text_seq("HISTORY_RETENTION_PERIOD", "="):
2331                    prop.set("retention_period", self._parse_retention_period())
2332
2333                self._match(TokenType.COMMA)
2334
2335        return prop
2336
2337    def _parse_data_deletion_property(self) -> exp.DataDeletionProperty:
2338        self._match(TokenType.EQ)
2339        on = self._match_text_seq("ON") or not self._match_text_seq("OFF")
2340        prop = self.expression(exp.DataDeletionProperty, on=on)
2341
2342        if self._match(TokenType.L_PAREN):
2343            while self._curr and not self._match(TokenType.R_PAREN):
2344                if self._match_text_seq("FILTER_COLUMN", "="):
2345                    prop.set("filter_column", self._parse_column())
2346                elif self._match_text_seq("RETENTION_PERIOD", "="):
2347                    prop.set("retention_period", self._parse_retention_period())
2348
2349                self._match(TokenType.COMMA)
2350
2351        return prop
2352
2353    def _parse_distributed_property(self) -> exp.DistributedByProperty:
2354        kind = "HASH"
2355        expressions: t.Optional[t.List[exp.Expression]] = None
2356        if self._match_text_seq("BY", "HASH"):
2357            expressions = self._parse_wrapped_csv(self._parse_id_var)
2358        elif self._match_text_seq("BY", "RANDOM"):
2359            kind = "RANDOM"
2360
2361        # If the BUCKETS keyword is not present, the number of buckets is AUTO
2362        buckets: t.Optional[exp.Expression] = None
2363        if self._match_text_seq("BUCKETS") and not self._match_text_seq("AUTO"):
2364            buckets = self._parse_number()
2365
2366        return self.expression(
2367            exp.DistributedByProperty,
2368            expressions=expressions,
2369            kind=kind,
2370            buckets=buckets,
2371            order=self._parse_order(),
2372        )
2373
2374    def _parse_composite_key_property(self, expr_type: t.Type[E]) -> E:
2375        self._match_text_seq("KEY")
2376        expressions = self._parse_wrapped_id_vars()
2377        return self.expression(expr_type, expressions=expressions)
2378
2379    def _parse_with_property(self) -> t.Optional[exp.Expression] | t.List[exp.Expression]:
2380        if self._match_text_seq("(", "SYSTEM_VERSIONING"):
2381            prop = self._parse_system_versioning_property(with_=True)
2382            self._match_r_paren()
2383            return prop
2384
2385        if self._match(TokenType.L_PAREN, advance=False):
2386            return self._parse_wrapped_properties()
2387
2388        if self._match_text_seq("JOURNAL"):
2389            return self._parse_withjournaltable()
2390
2391        if self._match_texts(self.VIEW_ATTRIBUTES):
2392            return self.expression(exp.ViewAttributeProperty, this=self._prev.text.upper())
2393
2394        if self._match_text_seq("DATA"):
2395            return self._parse_withdata(no=False)
2396        elif self._match_text_seq("NO", "DATA"):
2397            return self._parse_withdata(no=True)
2398
2399        if self._match(TokenType.SERDE_PROPERTIES, advance=False):
2400            return self._parse_serde_properties(with_=True)
2401
2402        if self._match(TokenType.SCHEMA):
2403            return self.expression(
2404                exp.WithSchemaBindingProperty,
2405                this=self._parse_var_from_options(self.SCHEMA_BINDING_OPTIONS),
2406            )
2407
2408        if self._match_texts(self.PROCEDURE_OPTIONS, advance=False):
2409            return self.expression(
2410                exp.WithProcedureOptions, expressions=self._parse_csv(self._parse_procedure_option)
2411            )
2412
2413        if not self._next:
2414            return None
2415
2416        return self._parse_withisolatedloading()
2417
2418    def _parse_procedure_option(self) -> exp.Expression | None:
2419        if self._match_text_seq("EXECUTE", "AS"):
2420            return self.expression(
2421                exp.ExecuteAsProperty,
2422                this=self._parse_var_from_options(self.EXECUTE_AS_OPTIONS, raise_unmatched=False)
2423                or self._parse_string(),
2424            )
2425
2426        return self._parse_var_from_options(self.PROCEDURE_OPTIONS)
2427
2428    # https://dev.mysql.com/doc/refman/8.0/en/create-view.html
2429    def _parse_definer(self) -> t.Optional[exp.DefinerProperty]:
2430        self._match(TokenType.EQ)
2431
2432        user = self._parse_id_var()
2433        self._match(TokenType.PARAMETER)
2434        host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text)
2435
2436        if not user or not host:
2437            return None
2438
2439        return exp.DefinerProperty(this=f"{user}@{host}")
2440
2441    def _parse_withjournaltable(self) -> exp.WithJournalTableProperty:
2442        self._match(TokenType.TABLE)
2443        self._match(TokenType.EQ)
2444        return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts())
2445
2446    def _parse_log(self, no: bool = False) -> exp.LogProperty:
2447        return self.expression(exp.LogProperty, no=no)
2448
2449    def _parse_journal(self, **kwargs) -> exp.JournalProperty:
2450        return self.expression(exp.JournalProperty, **kwargs)
2451
2452    def _parse_checksum(self) -> exp.ChecksumProperty:
2453        self._match(TokenType.EQ)
2454
2455        on = None
2456        if self._match(TokenType.ON):
2457            on = True
2458        elif self._match_text_seq("OFF"):
2459            on = False
2460
2461        return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT))
2462
2463    def _parse_cluster(self, wrapped: bool = False) -> exp.Cluster:
2464        return self.expression(
2465            exp.Cluster,
2466            expressions=(
2467                self._parse_wrapped_csv(self._parse_ordered)
2468                if wrapped
2469                else self._parse_csv(self._parse_ordered)
2470            ),
2471        )
2472
2473    def _parse_clustered_by(self) -> exp.ClusteredByProperty:
2474        self._match_text_seq("BY")
2475
2476        self._match_l_paren()
2477        expressions = self._parse_csv(self._parse_column)
2478        self._match_r_paren()
2479
2480        if self._match_text_seq("SORTED", "BY"):
2481            self._match_l_paren()
2482            sorted_by = self._parse_csv(self._parse_ordered)
2483            self._match_r_paren()
2484        else:
2485            sorted_by = None
2486
2487        self._match(TokenType.INTO)
2488        buckets = self._parse_number()
2489        self._match_text_seq("BUCKETS")
2490
2491        return self.expression(
2492            exp.ClusteredByProperty,
2493            expressions=expressions,
2494            sorted_by=sorted_by,
2495            buckets=buckets,
2496        )
2497
2498    def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]:
2499        if not self._match_text_seq("GRANTS"):
2500            self._retreat(self._index - 1)
2501            return None
2502
2503        return self.expression(exp.CopyGrantsProperty)
2504
2505    def _parse_freespace(self) -> exp.FreespaceProperty:
2506        self._match(TokenType.EQ)
2507        return self.expression(
2508            exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT)
2509        )
2510
2511    def _parse_mergeblockratio(
2512        self, no: bool = False, default: bool = False
2513    ) -> exp.MergeBlockRatioProperty:
2514        if self._match(TokenType.EQ):
2515            return self.expression(
2516                exp.MergeBlockRatioProperty,
2517                this=self._parse_number(),
2518                percent=self._match(TokenType.PERCENT),
2519            )
2520
2521        return self.expression(exp.MergeBlockRatioProperty, no=no, default=default)
2522
2523    def _parse_datablocksize(
2524        self,
2525        default: t.Optional[bool] = None,
2526        minimum: t.Optional[bool] = None,
2527        maximum: t.Optional[bool] = None,
2528    ) -> exp.DataBlocksizeProperty:
2529        self._match(TokenType.EQ)
2530        size = self._parse_number()
2531
2532        units = None
2533        if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")):
2534            units = self._prev.text
2535
2536        return self.expression(
2537            exp.DataBlocksizeProperty,
2538            size=size,
2539            units=units,
2540            default=default,
2541            minimum=minimum,
2542            maximum=maximum,
2543        )
2544
2545    def _parse_blockcompression(self) -> exp.BlockCompressionProperty:
2546        self._match(TokenType.EQ)
2547        always = self._match_text_seq("ALWAYS")
2548        manual = self._match_text_seq("MANUAL")
2549        never = self._match_text_seq("NEVER")
2550        default = self._match_text_seq("DEFAULT")
2551
2552        autotemp = None
2553        if self._match_text_seq("AUTOTEMP"):
2554            autotemp = self._parse_schema()
2555
2556        return self.expression(
2557            exp.BlockCompressionProperty,
2558            always=always,
2559            manual=manual,
2560            never=never,
2561            default=default,
2562            autotemp=autotemp,
2563        )
2564
2565    def _parse_withisolatedloading(self) -> t.Optional[exp.IsolatedLoadingProperty]:
2566        index = self._index
2567        no = self._match_text_seq("NO")
2568        concurrent = self._match_text_seq("CONCURRENT")
2569
2570        if not self._match_text_seq("ISOLATED", "LOADING"):
2571            self._retreat(index)
2572            return None
2573
2574        target = self._parse_var_from_options(self.ISOLATED_LOADING_OPTIONS, raise_unmatched=False)
2575        return self.expression(
2576            exp.IsolatedLoadingProperty, no=no, concurrent=concurrent, target=target
2577        )
2578
2579    def _parse_locking(self) -> exp.LockingProperty:
2580        if self._match(TokenType.TABLE):
2581            kind = "TABLE"
2582        elif self._match(TokenType.VIEW):
2583            kind = "VIEW"
2584        elif self._match(TokenType.ROW):
2585            kind = "ROW"
2586        elif self._match_text_seq("DATABASE"):
2587            kind = "DATABASE"
2588        else:
2589            kind = None
2590
2591        if kind in ("DATABASE", "TABLE", "VIEW"):
2592            this = self._parse_table_parts()
2593        else:
2594            this = None
2595
2596        if self._match(TokenType.FOR):
2597            for_or_in = "FOR"
2598        elif self._match(TokenType.IN):
2599            for_or_in = "IN"
2600        else:
2601            for_or_in = None
2602
2603        if self._match_text_seq("ACCESS"):
2604            lock_type = "ACCESS"
2605        elif self._match_texts(("EXCL", "EXCLUSIVE")):
2606            lock_type = "EXCLUSIVE"
2607        elif self._match_text_seq("SHARE"):
2608            lock_type = "SHARE"
2609        elif self._match_text_seq("READ"):
2610            lock_type = "READ"
2611        elif self._match_text_seq("WRITE"):
2612            lock_type = "WRITE"
2613        elif self._match_text_seq("CHECKSUM"):
2614            lock_type = "CHECKSUM"
2615        else:
2616            lock_type = None
2617
2618        override = self._match_text_seq("OVERRIDE")
2619
2620        return self.expression(
2621            exp.LockingProperty,
2622            this=this,
2623            kind=kind,
2624            for_or_in=for_or_in,
2625            lock_type=lock_type,
2626            override=override,
2627        )
2628
2629    def _parse_partition_by(self) -> t.List[exp.Expression]:
2630        if self._match(TokenType.PARTITION_BY):
2631            return self._parse_csv(self._parse_assignment)
2632        return []
2633
2634    def _parse_partition_bound_spec(self) -> exp.PartitionBoundSpec:
2635        def _parse_partition_bound_expr() -> t.Optional[exp.Expression]:
2636            if self._match_text_seq("MINVALUE"):
2637                return exp.var("MINVALUE")
2638            if self._match_text_seq("MAXVALUE"):
2639                return exp.var("MAXVALUE")
2640            return self._parse_bitwise()
2641
2642        this: t.Optional[exp.Expression | t.List[exp.Expression]] = None
2643        expression = None
2644        from_expressions = None
2645        to_expressions = None
2646
2647        if self._match(TokenType.IN):
2648            this = self._parse_wrapped_csv(self._parse_bitwise)
2649        elif self._match(TokenType.FROM):
2650            from_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2651            self._match_text_seq("TO")
2652            to_expressions = self._parse_wrapped_csv(_parse_partition_bound_expr)
2653        elif self._match_text_seq("WITH", "(", "MODULUS"):
2654            this = self._parse_number()
2655            self._match_text_seq(",", "REMAINDER")
2656            expression = self._parse_number()
2657            self._match_r_paren()
2658        else:
2659            self.raise_error("Failed to parse partition bound spec.")
2660
2661        return self.expression(
2662            exp.PartitionBoundSpec,
2663            this=this,
2664            expression=expression,
2665            from_expressions=from_expressions,
2666            to_expressions=to_expressions,
2667        )
2668
2669    # https://www.postgresql.org/docs/current/sql-createtable.html
2670    def _parse_partitioned_of(self) -> t.Optional[exp.PartitionedOfProperty]:
2671        if not self._match_text_seq("OF"):
2672            self._retreat(self._index - 1)
2673            return None
2674
2675        this = self._parse_table(schema=True)
2676
2677        if self._match(TokenType.DEFAULT):
2678            expression: exp.Var | exp.PartitionBoundSpec = exp.var("DEFAULT")
2679        elif self._match_text_seq("FOR", "VALUES"):
2680            expression = self._parse_partition_bound_spec()
2681        else:
2682            self.raise_error("Expecting either DEFAULT or FOR VALUES clause.")
2683
2684        return self.expression(exp.PartitionedOfProperty, this=this, expression=expression)
2685
2686    def _parse_partitioned_by(self) -> exp.PartitionedByProperty:
2687        self._match(TokenType.EQ)
2688        return self.expression(
2689            exp.PartitionedByProperty,
2690            this=self._parse_schema() or self._parse_bracket(self._parse_field()),
2691        )
2692
2693    def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty:
2694        if self._match_text_seq("AND", "STATISTICS"):
2695            statistics = True
2696        elif self._match_text_seq("AND", "NO", "STATISTICS"):
2697            statistics = False
2698        else:
2699            statistics = None
2700
2701        return self.expression(exp.WithDataProperty, no=no, statistics=statistics)
2702
2703    def _parse_contains_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2704        if self._match_text_seq("SQL"):
2705            return self.expression(exp.SqlReadWriteProperty, this="CONTAINS SQL")
2706        return None
2707
2708    def _parse_modifies_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2709        if self._match_text_seq("SQL", "DATA"):
2710            return self.expression(exp.SqlReadWriteProperty, this="MODIFIES SQL DATA")
2711        return None
2712
2713    def _parse_no_property(self) -> t.Optional[exp.Expression]:
2714        if self._match_text_seq("PRIMARY", "INDEX"):
2715            return exp.NoPrimaryIndexProperty()
2716        if self._match_text_seq("SQL"):
2717            return self.expression(exp.SqlReadWriteProperty, this="NO SQL")
2718        return None
2719
2720    def _parse_on_property(self) -> t.Optional[exp.Expression]:
2721        if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"):
2722            return exp.OnCommitProperty()
2723        if self._match_text_seq("COMMIT", "DELETE", "ROWS"):
2724            return exp.OnCommitProperty(delete=True)
2725        return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var()))
2726
2727    def _parse_reads_property(self) -> t.Optional[exp.SqlReadWriteProperty]:
2728        if self._match_text_seq("SQL", "DATA"):
2729            return self.expression(exp.SqlReadWriteProperty, this="READS SQL DATA")
2730        return None
2731
2732    def _parse_distkey(self) -> exp.DistKeyProperty:
2733        return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var))
2734
2735    def _parse_create_like(self) -> t.Optional[exp.LikeProperty]:
2736        table = self._parse_table(schema=True)
2737
2738        options = []
2739        while self._match_texts(("INCLUDING", "EXCLUDING")):
2740            this = self._prev.text.upper()
2741
2742            id_var = self._parse_id_var()
2743            if not id_var:
2744                return None
2745
2746            options.append(
2747                self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper()))
2748            )
2749
2750        return self.expression(exp.LikeProperty, this=table, expressions=options)
2751
2752    def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty:
2753        return self.expression(
2754            exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound
2755        )
2756
2757    def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty:
2758        self._match(TokenType.EQ)
2759        return self.expression(
2760            exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default
2761        )
2762
2763    def _parse_remote_with_connection(self) -> exp.RemoteWithConnectionModelProperty:
2764        self._match_text_seq("WITH", "CONNECTION")
2765        return self.expression(
2766            exp.RemoteWithConnectionModelProperty, this=self._parse_table_parts()
2767        )
2768
2769    def _parse_returns(self) -> exp.ReturnsProperty:
2770        value: t.Optional[exp.Expression]
2771        null = None
2772        is_table = self._match(TokenType.TABLE)
2773
2774        if is_table:
2775            if self._match(TokenType.LT):
2776                value = self.expression(
2777                    exp.Schema,
2778                    this="TABLE",
2779                    expressions=self._parse_csv(self._parse_struct_types),
2780                )
2781                if not self._match(TokenType.GT):
2782                    self.raise_error("Expecting >")
2783            else:
2784                value = self._parse_schema(exp.var("TABLE"))
2785        elif self._match_text_seq("NULL", "ON", "NULL", "INPUT"):
2786            null = True
2787            value = None
2788        else:
2789            value = self._parse_types()
2790
2791        return self.expression(exp.ReturnsProperty, this=value, is_table=is_table, null=null)
2792
2793    def _parse_describe(self) -> exp.Describe:
2794        kind = self._match_set(self.CREATABLES) and self._prev.text
2795        style = self._match_texts(self.DESCRIBE_STYLES) and self._prev.text.upper()
2796        if self._match(TokenType.DOT):
2797            style = None
2798            self._retreat(self._index - 2)
2799
2800        format = self._parse_property() if self._match(TokenType.FORMAT, advance=False) else None
2801
2802        if self._match_set(self.STATEMENT_PARSERS, advance=False):
2803            this = self._parse_statement()
2804        else:
2805            this = self._parse_table(schema=True)
2806
2807        properties = self._parse_properties()
2808        expressions = properties.expressions if properties else None
2809        partition = self._parse_partition()
2810        return self.expression(
2811            exp.Describe,
2812            this=this,
2813            style=style,
2814            kind=kind,
2815            expressions=expressions,
2816            partition=partition,
2817            format=format,
2818        )
2819
2820    def _parse_multitable_inserts(self, comments: t.Optional[t.List[str]]) -> exp.MultitableInserts:
2821        kind = self._prev.text.upper()
2822        expressions = []
2823
2824        def parse_conditional_insert() -> t.Optional[exp.ConditionalInsert]:
2825            if self._match(TokenType.WHEN):
2826                expression = self._parse_disjunction()
2827                self._match(TokenType.THEN)
2828            else:
2829                expression = None
2830
2831            else_ = self._match(TokenType.ELSE)
2832
2833            if not self._match(TokenType.INTO):
2834                return None
2835
2836            return self.expression(
2837                exp.ConditionalInsert,
2838                this=self.expression(
2839                    exp.Insert,
2840                    this=self._parse_table(schema=True),
2841                    expression=self._parse_derived_table_values(),
2842                ),
2843                expression=expression,
2844                else_=else_,
2845            )
2846
2847        expression = parse_conditional_insert()
2848        while expression is not None:
2849            expressions.append(expression)
2850            expression = parse_conditional_insert()
2851
2852        return self.expression(
2853            exp.MultitableInserts,
2854            kind=kind,
2855            comments=comments,
2856            expressions=expressions,
2857            source=self._parse_table(),
2858        )
2859
2860    def _parse_insert(self) -> t.Union[exp.Insert, exp.MultitableInserts]:
2861        comments = []
2862        hint = self._parse_hint()
2863        overwrite = self._match(TokenType.OVERWRITE)
2864        ignore = self._match(TokenType.IGNORE)
2865        local = self._match_text_seq("LOCAL")
2866        alternative = None
2867        is_function = None
2868
2869        if self._match_text_seq("DIRECTORY"):
2870            this: t.Optional[exp.Expression] = self.expression(
2871                exp.Directory,
2872                this=self._parse_var_or_string(),
2873                local=local,
2874                row_format=self._parse_row_format(match_row=True),
2875            )
2876        else:
2877            if self._match_set((TokenType.FIRST, TokenType.ALL)):
2878                comments += ensure_list(self._prev_comments)
2879                return self._parse_multitable_inserts(comments)
2880
2881            if self._match(TokenType.OR):
2882                alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text
2883
2884            self._match(TokenType.INTO)
2885            comments += ensure_list(self._prev_comments)
2886            self._match(TokenType.TABLE)
2887            is_function = self._match(TokenType.FUNCTION)
2888
2889            this = (
2890                self._parse_table(schema=True, parse_partition=True)
2891                if not is_function
2892                else self._parse_function()
2893            )
2894            if isinstance(this, exp.Table) and self._match(TokenType.ALIAS, advance=False):
2895                this.set("alias", self._parse_table_alias())
2896
2897        returning = self._parse_returning()
2898
2899        return self.expression(
2900            exp.Insert,
2901            comments=comments,
2902            hint=hint,
2903            is_function=is_function,
2904            this=this,
2905            stored=self._match_text_seq("STORED") and self._parse_stored(),
2906            by_name=self._match_text_seq("BY", "NAME"),
2907            exists=self._parse_exists(),
2908            where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) and self._parse_assignment(),
2909            partition=self._match(TokenType.PARTITION_BY) and self._parse_partitioned_by(),
2910            settings=self._match_text_seq("SETTINGS") and self._parse_settings_property(),
2911            expression=self._parse_derived_table_values() or self._parse_ddl_select(),
2912            conflict=self._parse_on_conflict(),
2913            returning=returning or self._parse_returning(),
2914            overwrite=overwrite,
2915            alternative=alternative,
2916            ignore=ignore,
2917            source=self._match(TokenType.TABLE) and self._parse_table(),
2918        )
2919
2920    def _parse_kill(self) -> exp.Kill:
2921        kind = exp.var(self._prev.text) if self._match_texts(("CONNECTION", "QUERY")) else None
2922
2923        return self.expression(
2924            exp.Kill,
2925            this=self._parse_primary(),
2926            kind=kind,
2927        )
2928
2929    def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]:
2930        conflict = self._match_text_seq("ON", "CONFLICT")
2931        duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY")
2932
2933        if not conflict and not duplicate:
2934            return None
2935
2936        conflict_keys = None
2937        constraint = None
2938
2939        if conflict:
2940            if self._match_text_seq("ON", "CONSTRAINT"):
2941                constraint = self._parse_id_var()
2942            elif self._match(TokenType.L_PAREN):
2943                conflict_keys = self._parse_csv(self._parse_id_var)
2944                self._match_r_paren()
2945
2946        action = self._parse_var_from_options(self.CONFLICT_ACTIONS)
2947        if self._prev.token_type == TokenType.UPDATE:
2948            self._match(TokenType.SET)
2949            expressions = self._parse_csv(self._parse_equality)
2950        else:
2951            expressions = None
2952
2953        return self.expression(
2954            exp.OnConflict,
2955            duplicate=duplicate,
2956            expressions=expressions,
2957            action=action,
2958            conflict_keys=conflict_keys,
2959            constraint=constraint,
2960            where=self._parse_where(),
2961        )
2962
2963    def _parse_returning(self) -> t.Optional[exp.Returning]:
2964        if not self._match(TokenType.RETURNING):
2965            return None
2966        return self.expression(
2967            exp.Returning,
2968            expressions=self._parse_csv(self._parse_expression),
2969            into=self._match(TokenType.INTO) and self._parse_table_part(),
2970        )
2971
2972    def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2973        if not self._match(TokenType.FORMAT):
2974            return None
2975        return self._parse_row_format()
2976
2977    def _parse_serde_properties(self, with_: bool = False) -> t.Optional[exp.SerdeProperties]:
2978        index = self._index
2979        with_ = with_ or self._match_text_seq("WITH")
2980
2981        if not self._match(TokenType.SERDE_PROPERTIES):
2982            self._retreat(index)
2983            return None
2984        return self.expression(
2985            exp.SerdeProperties,
2986            **{  # type: ignore
2987                "expressions": self._parse_wrapped_properties(),
2988                "with": with_,
2989            },
2990        )
2991
2992    def _parse_row_format(
2993        self, match_row: bool = False
2994    ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]:
2995        if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT):
2996            return None
2997
2998        if self._match_text_seq("SERDE"):
2999            this = self._parse_string()
3000
3001            serde_properties = self._parse_serde_properties()
3002
3003            return self.expression(
3004                exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties
3005            )
3006
3007        self._match_text_seq("DELIMITED")
3008
3009        kwargs = {}
3010
3011        if self._match_text_seq("FIELDS", "TERMINATED", "BY"):
3012            kwargs["fields"] = self._parse_string()
3013            if self._match_text_seq("ESCAPED", "BY"):
3014                kwargs["escaped"] = self._parse_string()
3015        if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"):
3016            kwargs["collection_items"] = self._parse_string()
3017        if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"):
3018            kwargs["map_keys"] = self._parse_string()
3019        if self._match_text_seq("LINES", "TERMINATED", "BY"):
3020            kwargs["lines"] = self._parse_string()
3021        if self._match_text_seq("NULL", "DEFINED", "AS"):
3022            kwargs["null"] = self._parse_string()
3023
3024        return self.expression(exp.RowFormatDelimitedProperty, **kwargs)  # type: ignore
3025
3026    def _parse_load(self) -> exp.LoadData | exp.Command:
3027        if self._match_text_seq("DATA"):
3028            local = self._match_text_seq("LOCAL")
3029            self._match_text_seq("INPATH")
3030            inpath = self._parse_string()
3031            overwrite = self._match(TokenType.OVERWRITE)
3032            self._match_pair(TokenType.INTO, TokenType.TABLE)
3033
3034            return self.expression(
3035                exp.LoadData,
3036                this=self._parse_table(schema=True),
3037                local=local,
3038                overwrite=overwrite,
3039                inpath=inpath,
3040                partition=self._parse_partition(),
3041                input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(),
3042                serde=self._match_text_seq("SERDE") and self._parse_string(),
3043            )
3044        return self._parse_as_command(self._prev)
3045
3046    def _parse_delete(self) -> exp.Delete:
3047        # This handles MySQL's "Multiple-Table Syntax"
3048        # https://dev.mysql.com/doc/refman/8.0/en/delete.html
3049        tables = None
3050        if not self._match(TokenType.FROM, advance=False):
3051            tables = self._parse_csv(self._parse_table) or None
3052
3053        returning = self._parse_returning()
3054
3055        return self.expression(
3056            exp.Delete,
3057            tables=tables,
3058            this=self._match(TokenType.FROM) and self._parse_table(joins=True),
3059            using=self._match(TokenType.USING) and self._parse_table(joins=True),
3060            cluster=self._match(TokenType.ON) and self._parse_on_property(),
3061            where=self._parse_where(),
3062            returning=returning or self._parse_returning(),
3063            limit=self._parse_limit(),
3064        )
3065
3066    def _parse_update(self) -> exp.Update:
3067        this = self._parse_table(joins=True, alias_tokens=self.UPDATE_ALIAS_TOKENS)
3068        expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality)
3069        returning = self._parse_returning()
3070        return self.expression(
3071            exp.Update,
3072            **{  # type: ignore
3073                "this": this,
3074                "expressions": expressions,
3075                "from": self._parse_from(joins=True),
3076                "where": self._parse_where(),
3077                "returning": returning or self._parse_returning(),
3078                "order": self._parse_order(),
3079                "limit": self._parse_limit(),
3080            },
3081        )
3082
3083    def _parse_use(self) -> exp.Use:
3084        return self.expression(
3085            exp.Use,
3086            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
3087            this=self._parse_table(schema=False),
3088        )
3089
3090    def _parse_uncache(self) -> exp.Uncache:
3091        if not self._match(TokenType.TABLE):
3092            self.raise_error("Expecting TABLE after UNCACHE")
3093
3094        return self.expression(
3095            exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True)
3096        )
3097
3098    def _parse_cache(self) -> exp.Cache:
3099        lazy = self._match_text_seq("LAZY")
3100        self._match(TokenType.TABLE)
3101        table = self._parse_table(schema=True)
3102
3103        options = []
3104        if self._match_text_seq("OPTIONS"):
3105            self._match_l_paren()
3106            k = self._parse_string()
3107            self._match(TokenType.EQ)
3108            v = self._parse_string()
3109            options = [k, v]
3110            self._match_r_paren()
3111
3112        self._match(TokenType.ALIAS)
3113        return self.expression(
3114            exp.Cache,
3115            this=table,
3116            lazy=lazy,
3117            options=options,
3118            expression=self._parse_select(nested=True),
3119        )
3120
3121    def _parse_partition(self) -> t.Optional[exp.Partition]:
3122        if not self._match_texts(self.PARTITION_KEYWORDS):
3123            return None
3124
3125        return self.expression(
3126            exp.Partition,
3127            subpartition=self._prev.text.upper() == "SUBPARTITION",
3128            expressions=self._parse_wrapped_csv(self._parse_assignment),
3129        )
3130
3131    def _parse_value(self, values: bool = True) -> t.Optional[exp.Tuple]:
3132        def _parse_value_expression() -> t.Optional[exp.Expression]:
3133            if self.dialect.SUPPORTS_VALUES_DEFAULT and self._match(TokenType.DEFAULT):
3134                return exp.var(self._prev.text.upper())
3135            return self._parse_expression()
3136
3137        if self._match(TokenType.L_PAREN):
3138            expressions = self._parse_csv(_parse_value_expression)
3139            self._match_r_paren()
3140            return self.expression(exp.Tuple, expressions=expressions)
3141
3142        # In some dialects we can have VALUES 1, 2 which results in 1 column & 2 rows.
3143        expression = self._parse_expression()
3144        if expression:
3145            return self.expression(exp.Tuple, expressions=[expression])
3146        return None
3147
3148    def _parse_projections(self) -> t.List[exp.Expression]:
3149        return self._parse_expressions()
3150
3151    def _parse_wrapped_select(self, table: bool = False) -> t.Optional[exp.Expression]:
3152        if self._match_set((TokenType.PIVOT, TokenType.UNPIVOT)):
3153            this: t.Optional[exp.Expression] = self._parse_simplified_pivot(
3154                is_unpivot=self._prev.token_type == TokenType.UNPIVOT
3155            )
3156        elif self._match(TokenType.FROM):
3157            from_ = self._parse_from(skip_from_token=True, consume_pipe=True)
3158            # Support parentheses for duckdb FROM-first syntax
3159            select = self._parse_select()
3160            if select:
3161                select.set("from", from_)
3162                this = select
3163            else:
3164                this = exp.select("*").from_(t.cast(exp.From, from_))
3165        else:
3166            this = (
3167                self._parse_table(consume_pipe=True)
3168                if table
3169                else self._parse_select(nested=True, parse_set_operation=False)
3170            )
3171
3172            # Transform exp.Values into a exp.Table to pass through parse_query_modifiers
3173            # in case a modifier (e.g. join) is following
3174            if table and isinstance(this, exp.Values) and this.alias:
3175                alias = this.args["alias"].pop()
3176                this = exp.Table(this=this, alias=alias)
3177
3178            this = self._parse_query_modifiers(self._parse_set_operations(this))
3179
3180        return this
3181
3182    def _parse_select(
3183        self,
3184        nested: bool = False,
3185        table: bool = False,
3186        parse_subquery_alias: bool = True,
3187        parse_set_operation: bool = True,
3188        consume_pipe: bool = True,
3189    ) -> t.Optional[exp.Expression]:
3190        query = self._parse_select_query(
3191            nested=nested,
3192            table=table,
3193            parse_subquery_alias=parse_subquery_alias,
3194            parse_set_operation=parse_set_operation,
3195        )
3196
3197        if (
3198            consume_pipe
3199            and self._match(TokenType.PIPE_GT, advance=False)
3200            and isinstance(query, exp.Query)
3201        ):
3202            query = self._parse_pipe_syntax_query(query)
3203            query = query.subquery(copy=False) if query and table else query
3204
3205        return query
3206
3207    def _parse_select_query(
3208        self,
3209        nested: bool = False,
3210        table: bool = False,
3211        parse_subquery_alias: bool = True,
3212        parse_set_operation: bool = True,
3213    ) -> t.Optional[exp.Expression]:
3214        cte = self._parse_with()
3215
3216        if cte:
3217            this = self._parse_statement()
3218
3219            if not this:
3220                self.raise_error("Failed to parse any statement following CTE")
3221                return cte
3222
3223            if "with" in this.arg_types:
3224                this.set("with", cte)
3225            else:
3226                self.raise_error(f"{this.key} does not support CTE")
3227                this = cte
3228
3229            return this
3230
3231        # duckdb supports leading with FROM x
3232        from_ = (
3233            self._parse_from(consume_pipe=True)
3234            if self._match(TokenType.FROM, advance=False)
3235            else None
3236        )
3237
3238        if self._match(TokenType.SELECT):
3239            comments = self._prev_comments
3240
3241            hint = self._parse_hint()
3242
3243            if self._next and not self._next.token_type == TokenType.DOT:
3244                all_ = self._match(TokenType.ALL)
3245                distinct = self._match_set(self.DISTINCT_TOKENS)
3246            else:
3247                all_, distinct = None, None
3248
3249            kind = (
3250                self._match(TokenType.ALIAS)
3251                and self._match_texts(("STRUCT", "VALUE"))
3252                and self._prev.text.upper()
3253            )
3254
3255            if distinct:
3256                distinct = self.expression(
3257                    exp.Distinct,
3258                    on=self._parse_value(values=False) if self._match(TokenType.ON) else None,
3259                )
3260
3261            if all_ and distinct:
3262                self.raise_error("Cannot specify both ALL and DISTINCT after SELECT")
3263
3264            operation_modifiers = []
3265            while self._curr and self._match_texts(self.OPERATION_MODIFIERS):
3266                operation_modifiers.append(exp.var(self._prev.text.upper()))
3267
3268            limit = self._parse_limit(top=True)
3269            projections = self._parse_projections()
3270
3271            this = self.expression(
3272                exp.Select,
3273                kind=kind,
3274                hint=hint,
3275                distinct=distinct,
3276                expressions=projections,
3277                limit=limit,
3278                operation_modifiers=operation_modifiers or None,
3279            )
3280            this.comments = comments
3281
3282            into = self._parse_into()
3283            if into:
3284                this.set("into", into)
3285
3286            if not from_:
3287                from_ = self._parse_from()
3288
3289            if from_:
3290                this.set("from", from_)
3291
3292            this = self._parse_query_modifiers(this)
3293        elif (table or nested) and self._match(TokenType.L_PAREN):
3294            this = self._parse_wrapped_select(table=table)
3295
3296            # We return early here so that the UNION isn't attached to the subquery by the
3297            # following call to _parse_set_operations, but instead becomes the parent node
3298            self._match_r_paren()
3299            return self._parse_subquery(this, parse_alias=parse_subquery_alias)
3300        elif self._match(TokenType.VALUES, advance=False):
3301            this = self._parse_derived_table_values()
3302        elif from_:
3303            this = exp.select("*").from_(from_.this, copy=False)
3304        elif self._match(TokenType.SUMMARIZE):
3305            table = self._match(TokenType.TABLE)
3306            this = self._parse_select() or self._parse_string() or self._parse_table()
3307            return self.expression(exp.Summarize, this=this, table=table)
3308        elif self._match(TokenType.DESCRIBE):
3309            this = self._parse_describe()
3310        elif self._match_text_seq("STREAM"):
3311            this = self._parse_function()
3312            if this:
3313                this = self.expression(exp.Stream, this=this)
3314            else:
3315                self._retreat(self._index - 1)
3316        else:
3317            this = None
3318
3319        return self._parse_set_operations(this) if parse_set_operation else this
3320
3321    def _parse_recursive_with_search(self) -> t.Optional[exp.RecursiveWithSearch]:
3322        self._match_text_seq("SEARCH")
3323
3324        kind = self._match_texts(self.RECURSIVE_CTE_SEARCH_KIND) and self._prev.text.upper()
3325
3326        if not kind:
3327            return None
3328
3329        self._match_text_seq("FIRST", "BY")
3330
3331        return self.expression(
3332            exp.RecursiveWithSearch,
3333            kind=kind,
3334            this=self._parse_id_var(),
3335            expression=self._match_text_seq("SET") and self._parse_id_var(),
3336            using=self._match_text_seq("USING") and self._parse_id_var(),
3337        )
3338
3339    def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]:
3340        if not skip_with_token and not self._match(TokenType.WITH):
3341            return None
3342
3343        comments = self._prev_comments
3344        recursive = self._match(TokenType.RECURSIVE)
3345
3346        last_comments = None
3347        expressions = []
3348        while True:
3349            cte = self._parse_cte()
3350            if isinstance(cte, exp.CTE):
3351                expressions.append(cte)
3352                if last_comments:
3353                    cte.add_comments(last_comments)
3354
3355            if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH):
3356                break
3357            else:
3358                self._match(TokenType.WITH)
3359
3360            last_comments = self._prev_comments
3361
3362        return self.expression(
3363            exp.With,
3364            comments=comments,
3365            expressions=expressions,
3366            recursive=recursive,
3367            search=self._parse_recursive_with_search(),
3368        )
3369
3370    def _parse_cte(self) -> t.Optional[exp.CTE]:
3371        index = self._index
3372
3373        alias = self._parse_table_alias(self.ID_VAR_TOKENS)
3374        if not alias or not alias.this:
3375            self.raise_error("Expected CTE to have alias")
3376
3377        if not self._match(TokenType.ALIAS) and not self.OPTIONAL_ALIAS_TOKEN_CTE:
3378            self._retreat(index)
3379            return None
3380
3381        comments = self._prev_comments
3382
3383        if self._match_text_seq("NOT", "MATERIALIZED"):
3384            materialized = False
3385        elif self._match_text_seq("MATERIALIZED"):
3386            materialized = True
3387        else:
3388            materialized = None
3389
3390        cte = self.expression(
3391            exp.CTE,
3392            this=self._parse_wrapped(self._parse_statement),
3393            alias=alias,
3394            materialized=materialized,
3395            comments=comments,
3396        )
3397
3398        values = cte.this
3399        if isinstance(values, exp.Values):
3400            if values.alias:
3401                cte.set("this", exp.select("*").from_(values))
3402            else:
3403                cte.set("this", exp.select("*").from_(exp.alias_(values, "_values", table=True)))
3404
3405        return cte
3406
3407    def _parse_table_alias(
3408        self, alias_tokens: t.Optional[t.Collection[TokenType]] = None
3409    ) -> t.Optional[exp.TableAlias]:
3410        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
3411        # so this section tries to parse the clause version and if it fails, it treats the token
3412        # as an identifier (alias)
3413        if self._can_parse_limit_or_offset():
3414            return None
3415
3416        any_token = self._match(TokenType.ALIAS)
3417        alias = (
3418            self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
3419            or self._parse_string_as_identifier()
3420        )
3421
3422        index = self._index
3423        if self._match(TokenType.L_PAREN):
3424            columns = self._parse_csv(self._parse_function_parameter)
3425            self._match_r_paren() if columns else self._retreat(index)
3426        else:
3427            columns = None
3428
3429        if not alias and not columns:
3430            return None
3431
3432        table_alias = self.expression(exp.TableAlias, this=alias, columns=columns)
3433
3434        # We bubble up comments from the Identifier to the TableAlias
3435        if isinstance(alias, exp.Identifier):
3436            table_alias.add_comments(alias.pop_comments())
3437
3438        return table_alias
3439
3440    def _parse_subquery(
3441        self, this: t.Optional[exp.Expression], parse_alias: bool = True
3442    ) -> t.Optional[exp.Subquery]:
3443        if not this:
3444            return None
3445
3446        return self.expression(
3447            exp.Subquery,
3448            this=this,
3449            pivots=self._parse_pivots(),
3450            alias=self._parse_table_alias() if parse_alias else None,
3451            sample=self._parse_table_sample(),
3452        )
3453
3454    def _implicit_unnests_to_explicit(self, this: E) -> E:
3455        from sqlglot.optimizer.normalize_identifiers import normalize_identifiers as _norm
3456
3457        refs = {_norm(this.args["from"].this.copy(), dialect=self.dialect).alias_or_name}
3458        for i, join in enumerate(this.args.get("joins") or []):
3459            table = join.this
3460            normalized_table = table.copy()
3461            normalized_table.meta["maybe_column"] = True
3462            normalized_table = _norm(normalized_table, dialect=self.dialect)
3463
3464            if isinstance(table, exp.Table) and not join.args.get("on"):
3465                if normalized_table.parts[0].name in refs:
3466                    table_as_column = table.to_column()
3467                    unnest = exp.Unnest(expressions=[table_as_column])
3468
3469                    # Table.to_column creates a parent Alias node that we want to convert to
3470                    # a TableAlias and attach to the Unnest, so it matches the parser's output
3471                    if isinstance(table.args.get("alias"), exp.TableAlias):
3472                        table_as_column.replace(table_as_column.this)
3473                        exp.alias_(unnest, None, table=[table.args["alias"].this], copy=False)
3474
3475                    table.replace(unnest)
3476
3477            refs.add(normalized_table.alias_or_name)
3478
3479        return this
3480
3481    def _parse_query_modifiers(
3482        self, this: t.Optional[exp.Expression]
3483    ) -> t.Optional[exp.Expression]:
3484        if isinstance(this, self.MODIFIABLES):
3485            for join in self._parse_joins():
3486                this.append("joins", join)
3487            for lateral in iter(self._parse_lateral, None):
3488                this.append("laterals", lateral)
3489
3490            while True:
3491                if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False):
3492                    parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type]
3493                    key, expression = parser(self)
3494
3495                    if expression:
3496                        this.set(key, expression)
3497                        if key == "limit":
3498                            offset = expression.args.pop("offset", None)
3499
3500                            if offset:
3501                                offset = exp.Offset(expression=offset)
3502                                this.set("offset", offset)
3503
3504                                limit_by_expressions = expression.expressions
3505                                expression.set("expressions", None)
3506                                offset.set("expressions", limit_by_expressions)
3507                        continue
3508                break
3509
3510        if self.SUPPORTS_IMPLICIT_UNNEST and this and this.args.get("from"):
3511            this = self._implicit_unnests_to_explicit(this)
3512
3513        return this
3514
3515    def _parse_hint_fallback_to_string(self) -> t.Optional[exp.Hint]:
3516        start = self._curr
3517        while self._curr:
3518            self._advance()
3519
3520        end = self._tokens[self._index - 1]
3521        return exp.Hint(expressions=[self._find_sql(start, end)])
3522
3523    def _parse_hint_function_call(self) -> t.Optional[exp.Expression]:
3524        return self._parse_function_call()
3525
3526    def _parse_hint_body(self) -> t.Optional[exp.Hint]:
3527        start_index = self._index
3528        should_fallback_to_string = False
3529
3530        hints = []
3531        try:
3532            for hint in iter(
3533                lambda: self._parse_csv(
3534                    lambda: self._parse_hint_function_call() or self._parse_var(upper=True),
3535                ),
3536                [],
3537            ):
3538                hints.extend(hint)
3539        except ParseError:
3540            should_fallback_to_string = True
3541
3542        if should_fallback_to_string or self._curr:
3543            self._retreat(start_index)
3544            return self._parse_hint_fallback_to_string()
3545
3546        return self.expression(exp.Hint, expressions=hints)
3547
3548    def _parse_hint(self) -> t.Optional[exp.Hint]:
3549        if self._match(TokenType.HINT) and self._prev_comments:
3550            return exp.maybe_parse(self._prev_comments[0], into=exp.Hint, dialect=self.dialect)
3551
3552        return None
3553
3554    def _parse_into(self) -> t.Optional[exp.Into]:
3555        if not self._match(TokenType.INTO):
3556            return None
3557
3558        temp = self._match(TokenType.TEMPORARY)
3559        unlogged = self._match_text_seq("UNLOGGED")
3560        self._match(TokenType.TABLE)
3561
3562        return self.expression(
3563            exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged
3564        )
3565
3566    def _parse_from(
3567        self,
3568        joins: bool = False,
3569        skip_from_token: bool = False,
3570        consume_pipe: bool = False,
3571    ) -> t.Optional[exp.From]:
3572        if not skip_from_token and not self._match(TokenType.FROM):
3573            return None
3574
3575        return self.expression(
3576            exp.From,
3577            comments=self._prev_comments,
3578            this=self._parse_table(joins=joins, consume_pipe=consume_pipe),
3579        )
3580
3581    def _parse_match_recognize_measure(self) -> exp.MatchRecognizeMeasure:
3582        return self.expression(
3583            exp.MatchRecognizeMeasure,
3584            window_frame=self._match_texts(("FINAL", "RUNNING")) and self._prev.text.upper(),
3585            this=self._parse_expression(),
3586        )
3587
3588    def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]:
3589        if not self._match(TokenType.MATCH_RECOGNIZE):
3590            return None
3591
3592        self._match_l_paren()
3593
3594        partition = self._parse_partition_by()
3595        order = self._parse_order()
3596
3597        measures = (
3598            self._parse_csv(self._parse_match_recognize_measure)
3599            if self._match_text_seq("MEASURES")
3600            else None
3601        )
3602
3603        if self._match_text_seq("ONE", "ROW", "PER", "MATCH"):
3604            rows = exp.var("ONE ROW PER MATCH")
3605        elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"):
3606            text = "ALL ROWS PER MATCH"
3607            if self._match_text_seq("SHOW", "EMPTY", "MATCHES"):
3608                text += " SHOW EMPTY MATCHES"
3609            elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"):
3610                text += " OMIT EMPTY MATCHES"
3611            elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"):
3612                text += " WITH UNMATCHED ROWS"
3613            rows = exp.var(text)
3614        else:
3615            rows = None
3616
3617        if self._match_text_seq("AFTER", "MATCH", "SKIP"):
3618            text = "AFTER MATCH SKIP"
3619            if self._match_text_seq("PAST", "LAST", "ROW"):
3620                text += " PAST LAST ROW"
3621            elif self._match_text_seq("TO", "NEXT", "ROW"):
3622                text += " TO NEXT ROW"
3623            elif self._match_text_seq("TO", "FIRST"):
3624                text += f" TO FIRST {self._advance_any().text}"  # type: ignore
3625            elif self._match_text_seq("TO", "LAST"):
3626                text += f" TO LAST {self._advance_any().text}"  # type: ignore
3627            after = exp.var(text)
3628        else:
3629            after = None
3630
3631        if self._match_text_seq("PATTERN"):
3632            self._match_l_paren()
3633
3634            if not self._curr:
3635                self.raise_error("Expecting )", self._curr)
3636
3637            paren = 1
3638            start = self._curr
3639
3640            while self._curr and paren > 0:
3641                if self._curr.token_type == TokenType.L_PAREN:
3642                    paren += 1
3643                if self._curr.token_type == TokenType.R_PAREN:
3644                    paren -= 1
3645
3646                end = self._prev
3647                self._advance()
3648
3649            if paren > 0:
3650                self.raise_error("Expecting )", self._curr)
3651
3652            pattern = exp.var(self._find_sql(start, end))
3653        else:
3654            pattern = None
3655
3656        define = (
3657            self._parse_csv(self._parse_name_as_expression)
3658            if self._match_text_seq("DEFINE")
3659            else None
3660        )
3661
3662        self._match_r_paren()
3663
3664        return self.expression(
3665            exp.MatchRecognize,
3666            partition_by=partition,
3667            order=order,
3668            measures=measures,
3669            rows=rows,
3670            after=after,
3671            pattern=pattern,
3672            define=define,
3673            alias=self._parse_table_alias(),
3674        )
3675
3676    def _parse_lateral(self) -> t.Optional[exp.Lateral]:
3677        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY)
3678        if not cross_apply and self._match_pair(TokenType.OUTER, TokenType.APPLY):
3679            cross_apply = False
3680
3681        if cross_apply is not None:
3682            this = self._parse_select(table=True)
3683            view = None
3684            outer = None
3685        elif self._match(TokenType.LATERAL):
3686            this = self._parse_select(table=True)
3687            view = self._match(TokenType.VIEW)
3688            outer = self._match(TokenType.OUTER)
3689        else:
3690            return None
3691
3692        if not this:
3693            this = (
3694                self._parse_unnest()
3695                or self._parse_function()
3696                or self._parse_id_var(any_token=False)
3697            )
3698
3699            while self._match(TokenType.DOT):
3700                this = exp.Dot(
3701                    this=this,
3702                    expression=self._parse_function() or self._parse_id_var(any_token=False),
3703                )
3704
3705        ordinality: t.Optional[bool] = None
3706
3707        if view:
3708            table = self._parse_id_var(any_token=False)
3709            columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else []
3710            table_alias: t.Optional[exp.TableAlias] = self.expression(
3711                exp.TableAlias, this=table, columns=columns
3712            )
3713        elif isinstance(this, (exp.Subquery, exp.Unnest)) and this.alias:
3714            # We move the alias from the lateral's child node to the lateral itself
3715            table_alias = this.args["alias"].pop()
3716        else:
3717            ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
3718            table_alias = self._parse_table_alias()
3719
3720        return self.expression(
3721            exp.Lateral,
3722            this=this,
3723            view=view,
3724            outer=outer,
3725            alias=table_alias,
3726            cross_apply=cross_apply,
3727            ordinality=ordinality,
3728        )
3729
3730    def _parse_join_parts(
3731        self,
3732    ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]:
3733        return (
3734            self._match_set(self.JOIN_METHODS) and self._prev,
3735            self._match_set(self.JOIN_SIDES) and self._prev,
3736            self._match_set(self.JOIN_KINDS) and self._prev,
3737        )
3738
3739    def _parse_using_identifiers(self) -> t.List[exp.Expression]:
3740        def _parse_column_as_identifier() -> t.Optional[exp.Expression]:
3741            this = self._parse_column()
3742            if isinstance(this, exp.Column):
3743                return this.this
3744            return this
3745
3746        return self._parse_wrapped_csv(_parse_column_as_identifier, optional=True)
3747
3748    def _parse_join(
3749        self, skip_join_token: bool = False, parse_bracket: bool = False
3750    ) -> t.Optional[exp.Join]:
3751        if self._match(TokenType.COMMA):
3752            table = self._try_parse(self._parse_table)
3753            cross_join = self.expression(exp.Join, this=table) if table else None
3754
3755            if cross_join and self.JOINS_HAVE_EQUAL_PRECEDENCE:
3756                cross_join.set("kind", "CROSS")
3757
3758            return cross_join
3759
3760        index = self._index
3761        method, side, kind = self._parse_join_parts()
3762        hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None
3763        join = self._match(TokenType.JOIN) or (kind and kind.token_type == TokenType.STRAIGHT_JOIN)
3764
3765        if not skip_join_token and not join:
3766            self._retreat(index)
3767            kind = None
3768            method = None
3769            side = None
3770
3771        outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False)
3772        cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False)
3773
3774        if not skip_join_token and not join and not outer_apply and not cross_apply:
3775            return None
3776
3777        kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)}
3778        if kind and kind.token_type == TokenType.ARRAY and self._match(TokenType.COMMA):
3779            kwargs["expressions"] = self._parse_csv(
3780                lambda: self._parse_table(parse_bracket=parse_bracket)
3781            )
3782
3783        if method:
3784            kwargs["method"] = method.text
3785        if side:
3786            kwargs["side"] = side.text
3787        if kind:
3788            kwargs["kind"] = kind.text
3789        if hint:
3790            kwargs["hint"] = hint
3791
3792        if self._match(TokenType.MATCH_CONDITION):
3793            kwargs["match_condition"] = self._parse_wrapped(self._parse_comparison)
3794
3795        if self._match(TokenType.ON):
3796            kwargs["on"] = self._parse_assignment()
3797        elif self._match(TokenType.USING):
3798            kwargs["using"] = self._parse_using_identifiers()
3799        elif (
3800            not (outer_apply or cross_apply)
3801            and not isinstance(kwargs["this"], exp.Unnest)
3802            and not (kind and kind.token_type in (TokenType.CROSS, TokenType.ARRAY))
3803        ):
3804            index = self._index
3805            joins: t.Optional[list] = list(self._parse_joins())
3806
3807            if joins and self._match(TokenType.ON):
3808                kwargs["on"] = self._parse_assignment()
3809            elif joins and self._match(TokenType.USING):
3810                kwargs["using"] = self._parse_using_identifiers()
3811            else:
3812                joins = None
3813                self._retreat(index)
3814
3815            kwargs["this"].set("joins", joins if joins else None)
3816
3817        kwargs["pivots"] = self._parse_pivots()
3818
3819        comments = [c for token in (method, side, kind) if token for c in token.comments]
3820        return self.expression(exp.Join, comments=comments, **kwargs)
3821
3822    def _parse_opclass(self) -> t.Optional[exp.Expression]:
3823        this = self._parse_assignment()
3824
3825        if self._match_texts(self.OPCLASS_FOLLOW_KEYWORDS, advance=False):
3826            return this
3827
3828        if not self._match_set(self.OPTYPE_FOLLOW_TOKENS, advance=False):
3829            return self.expression(exp.Opclass, this=this, expression=self._parse_table_parts())
3830
3831        return this
3832
3833    def _parse_index_params(self) -> exp.IndexParameters:
3834        using = self._parse_var(any_token=True) if self._match(TokenType.USING) else None
3835
3836        if self._match(TokenType.L_PAREN, advance=False):
3837            columns = self._parse_wrapped_csv(self._parse_with_operator)
3838        else:
3839            columns = None
3840
3841        include = self._parse_wrapped_id_vars() if self._match_text_seq("INCLUDE") else None
3842        partition_by = self._parse_partition_by()
3843        with_storage = self._match(TokenType.WITH) and self._parse_wrapped_properties()
3844        tablespace = (
3845            self._parse_var(any_token=True)
3846            if self._match_text_seq("USING", "INDEX", "TABLESPACE")
3847            else None
3848        )
3849        where = self._parse_where()
3850
3851        on = self._parse_field() if self._match(TokenType.ON) else None
3852
3853        return self.expression(
3854            exp.IndexParameters,
3855            using=using,
3856            columns=columns,
3857            include=include,
3858            partition_by=partition_by,
3859            where=where,
3860            with_storage=with_storage,
3861            tablespace=tablespace,
3862            on=on,
3863        )
3864
3865    def _parse_index(
3866        self, index: t.Optional[exp.Expression] = None, anonymous: bool = False
3867    ) -> t.Optional[exp.Index]:
3868        if index or anonymous:
3869            unique = None
3870            primary = None
3871            amp = None
3872
3873            self._match(TokenType.ON)
3874            self._match(TokenType.TABLE)  # hive
3875            table = self._parse_table_parts(schema=True)
3876        else:
3877            unique = self._match(TokenType.UNIQUE)
3878            primary = self._match_text_seq("PRIMARY")
3879            amp = self._match_text_seq("AMP")
3880
3881            if not self._match(TokenType.INDEX):
3882                return None
3883
3884            index = self._parse_id_var()
3885            table = None
3886
3887        params = self._parse_index_params()
3888
3889        return self.expression(
3890            exp.Index,
3891            this=index,
3892            table=table,
3893            unique=unique,
3894            primary=primary,
3895            amp=amp,
3896            params=params,
3897        )
3898
3899    def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]:
3900        hints: t.List[exp.Expression] = []
3901        if self._match_pair(TokenType.WITH, TokenType.L_PAREN):
3902            # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16
3903            hints.append(
3904                self.expression(
3905                    exp.WithTableHint,
3906                    expressions=self._parse_csv(
3907                        lambda: self._parse_function() or self._parse_var(any_token=True)
3908                    ),
3909                )
3910            )
3911            self._match_r_paren()
3912        else:
3913            # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html
3914            while self._match_set(self.TABLE_INDEX_HINT_TOKENS):
3915                hint = exp.IndexTableHint(this=self._prev.text.upper())
3916
3917                self._match_set((TokenType.INDEX, TokenType.KEY))
3918                if self._match(TokenType.FOR):
3919                    hint.set("target", self._advance_any() and self._prev.text.upper())
3920
3921                hint.set("expressions", self._parse_wrapped_id_vars())
3922                hints.append(hint)
3923
3924        return hints or None
3925
3926    def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]:
3927        return (
3928            (not schema and self._parse_function(optional_parens=False))
3929            or self._parse_id_var(any_token=False)
3930            or self._parse_string_as_identifier()
3931            or self._parse_placeholder()
3932        )
3933
3934    def _parse_table_parts(
3935        self, schema: bool = False, is_db_reference: bool = False, wildcard: bool = False
3936    ) -> exp.Table:
3937        catalog = None
3938        db = None
3939        table: t.Optional[exp.Expression | str] = self._parse_table_part(schema=schema)
3940
3941        while self._match(TokenType.DOT):
3942            if catalog:
3943                # This allows nesting the table in arbitrarily many dot expressions if needed
3944                table = self.expression(
3945                    exp.Dot, this=table, expression=self._parse_table_part(schema=schema)
3946                )
3947            else:
3948                catalog = db
3949                db = table
3950                # "" used for tsql FROM a..b case
3951                table = self._parse_table_part(schema=schema) or ""
3952
3953        if (
3954            wildcard
3955            and self._is_connected()
3956            and (isinstance(table, exp.Identifier) or not table)
3957            and self._match(TokenType.STAR)
3958        ):
3959            if isinstance(table, exp.Identifier):
3960                table.args["this"] += "*"
3961            else:
3962                table = exp.Identifier(this="*")
3963
3964        # We bubble up comments from the Identifier to the Table
3965        comments = table.pop_comments() if isinstance(table, exp.Expression) else None
3966
3967        if is_db_reference:
3968            catalog = db
3969            db = table
3970            table = None
3971
3972        if not table and not is_db_reference:
3973            self.raise_error(f"Expected table name but got {self._curr}")
3974        if not db and is_db_reference:
3975            self.raise_error(f"Expected database name but got {self._curr}")
3976
3977        table = self.expression(
3978            exp.Table,
3979            comments=comments,
3980            this=table,
3981            db=db,
3982            catalog=catalog,
3983        )
3984
3985        changes = self._parse_changes()
3986        if changes:
3987            table.set("changes", changes)
3988
3989        at_before = self._parse_historical_data()
3990        if at_before:
3991            table.set("when", at_before)
3992
3993        pivots = self._parse_pivots()
3994        if pivots:
3995            table.set("pivots", pivots)
3996
3997        return table
3998
3999    def _parse_table(
4000        self,
4001        schema: bool = False,
4002        joins: bool = False,
4003        alias_tokens: t.Optional[t.Collection[TokenType]] = None,
4004        parse_bracket: bool = False,
4005        is_db_reference: bool = False,
4006        parse_partition: bool = False,
4007        consume_pipe: bool = False,
4008    ) -> t.Optional[exp.Expression]:
4009        lateral = self._parse_lateral()
4010        if lateral:
4011            return lateral
4012
4013        unnest = self._parse_unnest()
4014        if unnest:
4015            return unnest
4016
4017        values = self._parse_derived_table_values()
4018        if values:
4019            return values
4020
4021        subquery = self._parse_select(table=True, consume_pipe=consume_pipe)
4022        if subquery:
4023            if not subquery.args.get("pivots"):
4024                subquery.set("pivots", self._parse_pivots())
4025            return subquery
4026
4027        bracket = parse_bracket and self._parse_bracket(None)
4028        bracket = self.expression(exp.Table, this=bracket) if bracket else None
4029
4030        rows_from = self._match_text_seq("ROWS", "FROM") and self._parse_wrapped_csv(
4031            self._parse_table
4032        )
4033        rows_from = self.expression(exp.Table, rows_from=rows_from) if rows_from else None
4034
4035        only = self._match(TokenType.ONLY)
4036
4037        this = t.cast(
4038            exp.Expression,
4039            bracket
4040            or rows_from
4041            or self._parse_bracket(
4042                self._parse_table_parts(schema=schema, is_db_reference=is_db_reference)
4043            ),
4044        )
4045
4046        if only:
4047            this.set("only", only)
4048
4049        # Postgres supports a wildcard (table) suffix operator, which is a no-op in this context
4050        self._match_text_seq("*")
4051
4052        parse_partition = parse_partition or self.SUPPORTS_PARTITION_SELECTION
4053        if parse_partition and self._match(TokenType.PARTITION, advance=False):
4054            this.set("partition", self._parse_partition())
4055
4056        if schema:
4057            return self._parse_schema(this=this)
4058
4059        version = self._parse_version()
4060
4061        if version:
4062            this.set("version", version)
4063
4064        if self.dialect.ALIAS_POST_TABLESAMPLE:
4065            this.set("sample", self._parse_table_sample())
4066
4067        alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
4068        if alias:
4069            this.set("alias", alias)
4070
4071        if isinstance(this, exp.Table) and self._match_text_seq("AT"):
4072            return self.expression(
4073                exp.AtIndex, this=this.to_column(copy=False), expression=self._parse_id_var()
4074            )
4075
4076        this.set("hints", self._parse_table_hints())
4077
4078        if not this.args.get("pivots"):
4079            this.set("pivots", self._parse_pivots())
4080
4081        if not self.dialect.ALIAS_POST_TABLESAMPLE:
4082            this.set("sample", self._parse_table_sample())
4083
4084        if joins:
4085            for join in self._parse_joins():
4086                this.append("joins", join)
4087
4088        if self._match_pair(TokenType.WITH, TokenType.ORDINALITY):
4089            this.set("ordinality", True)
4090            this.set("alias", self._parse_table_alias())
4091
4092        return this
4093
4094    def _parse_version(self) -> t.Optional[exp.Version]:
4095        if self._match(TokenType.TIMESTAMP_SNAPSHOT):
4096            this = "TIMESTAMP"
4097        elif self._match(TokenType.VERSION_SNAPSHOT):
4098            this = "VERSION"
4099        else:
4100            return None
4101
4102        if self._match_set((TokenType.FROM, TokenType.BETWEEN)):
4103            kind = self._prev.text.upper()
4104            start = self._parse_bitwise()
4105            self._match_texts(("TO", "AND"))
4106            end = self._parse_bitwise()
4107            expression: t.Optional[exp.Expression] = self.expression(
4108                exp.Tuple, expressions=[start, end]
4109            )
4110        elif self._match_text_seq("CONTAINED", "IN"):
4111            kind = "CONTAINED IN"
4112            expression = self.expression(
4113                exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise)
4114            )
4115        elif self._match(TokenType.ALL):
4116            kind = "ALL"
4117            expression = None
4118        else:
4119            self._match_text_seq("AS", "OF")
4120            kind = "AS OF"
4121            expression = self._parse_type()
4122
4123        return self.expression(exp.Version, this=this, expression=expression, kind=kind)
4124
4125    def _parse_historical_data(self) -> t.Optional[exp.HistoricalData]:
4126        # https://docs.snowflake.com/en/sql-reference/constructs/at-before
4127        index = self._index
4128        historical_data = None
4129        if self._match_texts(self.HISTORICAL_DATA_PREFIX):
4130            this = self._prev.text.upper()
4131            kind = (
4132                self._match(TokenType.L_PAREN)
4133                and self._match_texts(self.HISTORICAL_DATA_KIND)
4134                and self._prev.text.upper()
4135            )
4136            expression = self._match(TokenType.FARROW) and self._parse_bitwise()
4137
4138            if expression:
4139                self._match_r_paren()
4140                historical_data = self.expression(
4141                    exp.HistoricalData, this=this, kind=kind, expression=expression
4142                )
4143            else:
4144                self._retreat(index)
4145
4146        return historical_data
4147
4148    def _parse_changes(self) -> t.Optional[exp.Changes]:
4149        if not self._match_text_seq("CHANGES", "(", "INFORMATION", "=>"):
4150            return None
4151
4152        information = self._parse_var(any_token=True)
4153        self._match_r_paren()
4154
4155        return self.expression(
4156            exp.Changes,
4157            information=information,
4158            at_before=self._parse_historical_data(),
4159            end=self._parse_historical_data(),
4160        )
4161
4162    def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]:
4163        if not self._match(TokenType.UNNEST):
4164            return None
4165
4166        expressions = self._parse_wrapped_csv(self._parse_equality)
4167        offset = self._match_pair(TokenType.WITH, TokenType.ORDINALITY)
4168
4169        alias = self._parse_table_alias() if with_alias else None
4170
4171        if alias:
4172            if self.dialect.UNNEST_COLUMN_ONLY:
4173                if alias.args.get("columns"):
4174                    self.raise_error("Unexpected extra column alias in unnest.")
4175
4176                alias.set("columns", [alias.this])
4177                alias.set("this", None)
4178
4179            columns = alias.args.get("columns") or []
4180            if offset and len(expressions) < len(columns):
4181                offset = columns.pop()
4182
4183        if not offset and self._match_pair(TokenType.WITH, TokenType.OFFSET):
4184            self._match(TokenType.ALIAS)
4185            offset = self._parse_id_var(
4186                any_token=False, tokens=self.UNNEST_OFFSET_ALIAS_TOKENS
4187            ) or exp.to_identifier("offset")
4188
4189        return self.expression(exp.Unnest, expressions=expressions, alias=alias, offset=offset)
4190
4191    def _parse_derived_table_values(self) -> t.Optional[exp.Values]:
4192        is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES)
4193        if not is_derived and not (
4194            # ClickHouse's `FORMAT Values` is equivalent to `VALUES`
4195            self._match_text_seq("VALUES") or self._match_text_seq("FORMAT", "VALUES")
4196        ):
4197            return None
4198
4199        expressions = self._parse_csv(self._parse_value)
4200        alias = self._parse_table_alias()
4201
4202        if is_derived:
4203            self._match_r_paren()
4204
4205        return self.expression(
4206            exp.Values, expressions=expressions, alias=alias or self._parse_table_alias()
4207        )
4208
4209    def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]:
4210        if not self._match(TokenType.TABLE_SAMPLE) and not (
4211            as_modifier and self._match_text_seq("USING", "SAMPLE")
4212        ):
4213            return None
4214
4215        bucket_numerator = None
4216        bucket_denominator = None
4217        bucket_field = None
4218        percent = None
4219        size = None
4220        seed = None
4221
4222        method = self._parse_var(tokens=(TokenType.ROW,), upper=True)
4223        matched_l_paren = self._match(TokenType.L_PAREN)
4224
4225        if self.TABLESAMPLE_CSV:
4226            num = None
4227            expressions = self._parse_csv(self._parse_primary)
4228        else:
4229            expressions = None
4230            num = (
4231                self._parse_factor()
4232                if self._match(TokenType.NUMBER, advance=False)
4233                else self._parse_primary() or self._parse_placeholder()
4234            )
4235
4236        if self._match_text_seq("BUCKET"):
4237            bucket_numerator = self._parse_number()
4238            self._match_text_seq("OUT", "OF")
4239            bucket_denominator = bucket_denominator = self._parse_number()
4240            self._match(TokenType.ON)
4241            bucket_field = self._parse_field()
4242        elif self._match_set((TokenType.PERCENT, TokenType.MOD)):
4243            percent = num
4244        elif self._match(TokenType.ROWS) or not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT:
4245            size = num
4246        else:
4247            percent = num
4248
4249        if matched_l_paren:
4250            self._match_r_paren()
4251
4252        if self._match(TokenType.L_PAREN):
4253            method = self._parse_var(upper=True)
4254            seed = self._match(TokenType.COMMA) and self._parse_number()
4255            self._match_r_paren()
4256        elif self._match_texts(("SEED", "REPEATABLE")):
4257            seed = self._parse_wrapped(self._parse_number)
4258
4259        if not method and self.DEFAULT_SAMPLING_METHOD:
4260            method = exp.var(self.DEFAULT_SAMPLING_METHOD)
4261
4262        return self.expression(
4263            exp.TableSample,
4264            expressions=expressions,
4265            method=method,
4266            bucket_numerator=bucket_numerator,
4267            bucket_denominator=bucket_denominator,
4268            bucket_field=bucket_field,
4269            percent=percent,
4270            size=size,
4271            seed=seed,
4272        )
4273
4274    def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]:
4275        return list(iter(self._parse_pivot, None)) or None
4276
4277    def _parse_joins(self) -> t.Iterator[exp.Join]:
4278        return iter(self._parse_join, None)
4279
4280    def _parse_unpivot_columns(self) -> t.Optional[exp.UnpivotColumns]:
4281        if not self._match(TokenType.INTO):
4282            return None
4283
4284        return self.expression(
4285            exp.UnpivotColumns,
4286            this=self._match_text_seq("NAME") and self._parse_column(),
4287            expressions=self._match_text_seq("VALUE") and self._parse_csv(self._parse_column),
4288        )
4289
4290    # https://duckdb.org/docs/sql/statements/pivot
4291    def _parse_simplified_pivot(self, is_unpivot: t.Optional[bool] = None) -> exp.Pivot:
4292        def _parse_on() -> t.Optional[exp.Expression]:
4293            this = self._parse_bitwise()
4294
4295            if self._match(TokenType.IN):
4296                # PIVOT ... ON col IN (row_val1, row_val2)
4297                return self._parse_in(this)
4298            if self._match(TokenType.ALIAS, advance=False):
4299                # UNPIVOT ... ON (col1, col2, col3) AS row_val
4300                return self._parse_alias(this)
4301
4302            return this
4303
4304        this = self._parse_table()
4305        expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on)
4306        into = self._parse_unpivot_columns()
4307        using = self._match(TokenType.USING) and self._parse_csv(
4308            lambda: self._parse_alias(self._parse_function())
4309        )
4310        group = self._parse_group()
4311
4312        return self.expression(
4313            exp.Pivot,
4314            this=this,
4315            expressions=expressions,
4316            using=using,
4317            group=group,
4318            unpivot=is_unpivot,
4319            into=into,
4320        )
4321
4322    def _parse_pivot_in(self) -> exp.In:
4323        def _parse_aliased_expression() -> t.Optional[exp.Expression]:
4324            this = self._parse_select_or_expression()
4325
4326            self._match(TokenType.ALIAS)
4327            alias = self._parse_bitwise()
4328            if alias:
4329                if isinstance(alias, exp.Column) and not alias.db:
4330                    alias = alias.this
4331                return self.expression(exp.PivotAlias, this=this, alias=alias)
4332
4333            return this
4334
4335        value = self._parse_column()
4336
4337        if not self._match_pair(TokenType.IN, TokenType.L_PAREN):
4338            self.raise_error("Expecting IN (")
4339
4340        if self._match(TokenType.ANY):
4341            exprs: t.List[exp.Expression] = ensure_list(exp.PivotAny(this=self._parse_order()))
4342        else:
4343            exprs = self._parse_csv(_parse_aliased_expression)
4344
4345        self._match_r_paren()
4346        return self.expression(exp.In, this=value, expressions=exprs)
4347
4348    def _parse_pivot(self) -> t.Optional[exp.Pivot]:
4349        index = self._index
4350        include_nulls = None
4351
4352        if self._match(TokenType.PIVOT):
4353            unpivot = False
4354        elif self._match(TokenType.UNPIVOT):
4355            unpivot = True
4356
4357            # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax
4358            if self._match_text_seq("INCLUDE", "NULLS"):
4359                include_nulls = True
4360            elif self._match_text_seq("EXCLUDE", "NULLS"):
4361                include_nulls = False
4362        else:
4363            return None
4364
4365        expressions = []
4366
4367        if not self._match(TokenType.L_PAREN):
4368            self._retreat(index)
4369            return None
4370
4371        if unpivot:
4372            expressions = self._parse_csv(self._parse_column)
4373        else:
4374            expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function()))
4375
4376        if not expressions:
4377            self.raise_error("Failed to parse PIVOT's aggregation list")
4378
4379        if not self._match(TokenType.FOR):
4380            self.raise_error("Expecting FOR")
4381
4382        fields = []
4383        while True:
4384            field = self._try_parse(self._parse_pivot_in)
4385            if not field:
4386                break
4387            fields.append(field)
4388
4389        default_on_null = self._match_text_seq("DEFAULT", "ON", "NULL") and self._parse_wrapped(
4390            self._parse_bitwise
4391        )
4392
4393        group = self._parse_group()
4394
4395        self._match_r_paren()
4396
4397        pivot = self.expression(
4398            exp.Pivot,
4399            expressions=expressions,
4400            fields=fields,
4401            unpivot=unpivot,
4402            include_nulls=include_nulls,
4403            default_on_null=default_on_null,
4404            group=group,
4405        )
4406
4407        if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False):
4408            pivot.set("alias", self._parse_table_alias())
4409
4410        if not unpivot:
4411            names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions))
4412
4413            columns: t.List[exp.Expression] = []
4414            all_fields = []
4415            for pivot_field in pivot.fields:
4416                pivot_field_expressions = pivot_field.expressions
4417
4418                # The `PivotAny` expression corresponds to `ANY ORDER BY <column>`; we can't infer in this case.
4419                if isinstance(seq_get(pivot_field_expressions, 0), exp.PivotAny):
4420                    continue
4421
4422                all_fields.append(
4423                    [
4424                        fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name
4425                        for fld in pivot_field_expressions
4426                    ]
4427                )
4428
4429            if all_fields:
4430                if names:
4431                    all_fields.append(names)
4432
4433                # Generate all possible combinations of the pivot columns
4434                # e.g PIVOT(sum(...) as total FOR year IN (2000, 2010) FOR country IN ('NL', 'US'))
4435                # generates the product between [[2000, 2010], ['NL', 'US'], ['total']]
4436                for fld_parts_tuple in itertools.product(*all_fields):
4437                    fld_parts = list(fld_parts_tuple)
4438
4439                    if names and self.PREFIXED_PIVOT_COLUMNS:
4440                        # Move the "name" to the front of the list
4441                        fld_parts.insert(0, fld_parts.pop(-1))
4442
4443                    columns.append(exp.to_identifier("_".join(fld_parts)))
4444
4445            pivot.set("columns", columns)
4446
4447        return pivot
4448
4449    def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]:
4450        return [agg.alias for agg in aggregations if agg.alias]
4451
4452    def _parse_prewhere(self, skip_where_token: bool = False) -> t.Optional[exp.PreWhere]:
4453        if not skip_where_token and not self._match(TokenType.PREWHERE):
4454            return None
4455
4456        return self.expression(
4457            exp.PreWhere, comments=self._prev_comments, this=self._parse_assignment()
4458        )
4459
4460    def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]:
4461        if not skip_where_token and not self._match(TokenType.WHERE):
4462            return None
4463
4464        return self.expression(
4465            exp.Where, comments=self._prev_comments, this=self._parse_assignment()
4466        )
4467
4468    def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]:
4469        if not skip_group_by_token and not self._match(TokenType.GROUP_BY):
4470            return None
4471
4472        elements: t.Dict[str, t.Any] = defaultdict(list)
4473
4474        if self._match(TokenType.ALL):
4475            elements["all"] = True
4476        elif self._match(TokenType.DISTINCT):
4477            elements["all"] = False
4478
4479        while True:
4480            index = self._index
4481
4482            elements["expressions"].extend(
4483                self._parse_csv(
4484                    lambda: None
4485                    if self._match_set((TokenType.CUBE, TokenType.ROLLUP), advance=False)
4486                    else self._parse_assignment()
4487                )
4488            )
4489
4490            before_with_index = self._index
4491            with_prefix = self._match(TokenType.WITH)
4492
4493            if self._match(TokenType.ROLLUP):
4494                elements["rollup"].append(
4495                    self._parse_cube_or_rollup(exp.Rollup, with_prefix=with_prefix)
4496                )
4497            elif self._match(TokenType.CUBE):
4498                elements["cube"].append(
4499                    self._parse_cube_or_rollup(exp.Cube, with_prefix=with_prefix)
4500                )
4501            elif self._match(TokenType.GROUPING_SETS):
4502                elements["grouping_sets"].append(
4503                    self.expression(
4504                        exp.GroupingSets,
4505                        expressions=self._parse_wrapped_csv(self._parse_grouping_set),
4506                    )
4507                )
4508            elif self._match_text_seq("TOTALS"):
4509                elements["totals"] = True  # type: ignore
4510
4511            if before_with_index <= self._index <= before_with_index + 1:
4512                self._retreat(before_with_index)
4513                break
4514
4515            if index == self._index:
4516                break
4517
4518        return self.expression(exp.Group, **elements)  # type: ignore
4519
4520    def _parse_cube_or_rollup(self, kind: t.Type[E], with_prefix: bool = False) -> E:
4521        return self.expression(
4522            kind, expressions=[] if with_prefix else self._parse_wrapped_csv(self._parse_column)
4523        )
4524
4525    def _parse_grouping_set(self) -> t.Optional[exp.Expression]:
4526        if self._match(TokenType.L_PAREN):
4527            grouping_set = self._parse_csv(self._parse_column)
4528            self._match_r_paren()
4529            return self.expression(exp.Tuple, expressions=grouping_set)
4530
4531        return self._parse_column()
4532
4533    def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]:
4534        if not skip_having_token and not self._match(TokenType.HAVING):
4535            return None
4536        return self.expression(exp.Having, this=self._parse_assignment())
4537
4538    def _parse_qualify(self) -> t.Optional[exp.Qualify]:
4539        if not self._match(TokenType.QUALIFY):
4540            return None
4541        return self.expression(exp.Qualify, this=self._parse_assignment())
4542
4543    def _parse_connect_with_prior(self) -> t.Optional[exp.Expression]:
4544        self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression(
4545            exp.Prior, this=self._parse_bitwise()
4546        )
4547        connect = self._parse_assignment()
4548        self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR")
4549        return connect
4550
4551    def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]:
4552        if skip_start_token:
4553            start = None
4554        elif self._match(TokenType.START_WITH):
4555            start = self._parse_assignment()
4556        else:
4557            return None
4558
4559        self._match(TokenType.CONNECT_BY)
4560        nocycle = self._match_text_seq("NOCYCLE")
4561        connect = self._parse_connect_with_prior()
4562
4563        if not start and self._match(TokenType.START_WITH):
4564            start = self._parse_assignment()
4565
4566        return self.expression(exp.Connect, start=start, connect=connect, nocycle=nocycle)
4567
4568    def _parse_name_as_expression(self) -> t.Optional[exp.Expression]:
4569        this = self._parse_id_var(any_token=True)
4570        if self._match(TokenType.ALIAS):
4571            this = self.expression(exp.Alias, alias=this, this=self._parse_assignment())
4572        return this
4573
4574    def _parse_interpolate(self) -> t.Optional[t.List[exp.Expression]]:
4575        if self._match_text_seq("INTERPOLATE"):
4576            return self._parse_wrapped_csv(self._parse_name_as_expression)
4577        return None
4578
4579    def _parse_order(
4580        self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False
4581    ) -> t.Optional[exp.Expression]:
4582        siblings = None
4583        if not skip_order_token and not self._match(TokenType.ORDER_BY):
4584            if not self._match(TokenType.ORDER_SIBLINGS_BY):
4585                return this
4586
4587            siblings = True
4588
4589        return self.expression(
4590            exp.Order,
4591            this=this,
4592            expressions=self._parse_csv(self._parse_ordered),
4593            siblings=siblings,
4594        )
4595
4596    def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]:
4597        if not self._match(token):
4598            return None
4599        return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered))
4600
4601    def _parse_ordered(
4602        self, parse_method: t.Optional[t.Callable] = None
4603    ) -> t.Optional[exp.Ordered]:
4604        this = parse_method() if parse_method else self._parse_assignment()
4605        if not this:
4606            return None
4607
4608        if this.name.upper() == "ALL" and self.dialect.SUPPORTS_ORDER_BY_ALL:
4609            this = exp.var("ALL")
4610
4611        asc = self._match(TokenType.ASC)
4612        desc = self._match(TokenType.DESC) or (asc and False)
4613
4614        is_nulls_first = self._match_text_seq("NULLS", "FIRST")
4615        is_nulls_last = self._match_text_seq("NULLS", "LAST")
4616
4617        nulls_first = is_nulls_first or False
4618        explicitly_null_ordered = is_nulls_first or is_nulls_last
4619
4620        if (
4621            not explicitly_null_ordered
4622            and (
4623                (not desc and self.dialect.NULL_ORDERING == "nulls_are_small")
4624                or (desc and self.dialect.NULL_ORDERING != "nulls_are_small")
4625            )
4626            and self.dialect.NULL_ORDERING != "nulls_are_last"
4627        ):
4628            nulls_first = True
4629
4630        if self._match_text_seq("WITH", "FILL"):
4631            with_fill = self.expression(
4632                exp.WithFill,
4633                **{  # type: ignore
4634                    "from": self._match(TokenType.FROM) and self._parse_bitwise(),
4635                    "to": self._match_text_seq("TO") and self._parse_bitwise(),
4636                    "step": self._match_text_seq("STEP") and self._parse_bitwise(),
4637                    "interpolate": self._parse_interpolate(),
4638                },
4639            )
4640        else:
4641            with_fill = None
4642
4643        return self.expression(
4644            exp.Ordered, this=this, desc=desc, nulls_first=nulls_first, with_fill=with_fill
4645        )
4646
4647    def _parse_limit_options(self) -> exp.LimitOptions:
4648        percent = self._match(TokenType.PERCENT)
4649        rows = self._match_set((TokenType.ROW, TokenType.ROWS))
4650        self._match_text_seq("ONLY")
4651        with_ties = self._match_text_seq("WITH", "TIES")
4652        return self.expression(exp.LimitOptions, percent=percent, rows=rows, with_ties=with_ties)
4653
4654    def _parse_limit(
4655        self,
4656        this: t.Optional[exp.Expression] = None,
4657        top: bool = False,
4658        skip_limit_token: bool = False,
4659    ) -> t.Optional[exp.Expression]:
4660        if skip_limit_token or self._match(TokenType.TOP if top else TokenType.LIMIT):
4661            comments = self._prev_comments
4662            if top:
4663                limit_paren = self._match(TokenType.L_PAREN)
4664                expression = self._parse_term() if limit_paren else self._parse_number()
4665
4666                if limit_paren:
4667                    self._match_r_paren()
4668
4669                limit_options = self._parse_limit_options()
4670            else:
4671                limit_options = None
4672                expression = self._parse_term()
4673
4674            if self._match(TokenType.COMMA):
4675                offset = expression
4676                expression = self._parse_term()
4677            else:
4678                offset = None
4679
4680            limit_exp = self.expression(
4681                exp.Limit,
4682                this=this,
4683                expression=expression,
4684                offset=offset,
4685                comments=comments,
4686                limit_options=limit_options,
4687                expressions=self._parse_limit_by(),
4688            )
4689
4690            return limit_exp
4691
4692        if self._match(TokenType.FETCH):
4693            direction = self._match_set((TokenType.FIRST, TokenType.NEXT))
4694            direction = self._prev.text.upper() if direction else "FIRST"
4695
4696            count = self._parse_field(tokens=self.FETCH_TOKENS)
4697
4698            return self.expression(
4699                exp.Fetch,
4700                direction=direction,
4701                count=count,
4702                limit_options=self._parse_limit_options(),
4703            )
4704
4705        return this
4706
4707    def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4708        if not self._match(TokenType.OFFSET):
4709            return this
4710
4711        count = self._parse_term()
4712        self._match_set((TokenType.ROW, TokenType.ROWS))
4713
4714        return self.expression(
4715            exp.Offset, this=this, expression=count, expressions=self._parse_limit_by()
4716        )
4717
4718    def _can_parse_limit_or_offset(self) -> bool:
4719        if not self._match_set(self.AMBIGUOUS_ALIAS_TOKENS, advance=False):
4720            return False
4721
4722        index = self._index
4723        result = bool(
4724            self._try_parse(self._parse_limit, retreat=True)
4725            or self._try_parse(self._parse_offset, retreat=True)
4726        )
4727        self._retreat(index)
4728        return result
4729
4730    def _parse_limit_by(self) -> t.Optional[t.List[exp.Expression]]:
4731        return self._match_text_seq("BY") and self._parse_csv(self._parse_bitwise)
4732
4733    def _parse_locks(self) -> t.List[exp.Lock]:
4734        locks = []
4735        while True:
4736            if self._match_text_seq("FOR", "UPDATE"):
4737                update = True
4738            elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq(
4739                "LOCK", "IN", "SHARE", "MODE"
4740            ):
4741                update = False
4742            else:
4743                break
4744
4745            expressions = None
4746            if self._match_text_seq("OF"):
4747                expressions = self._parse_csv(lambda: self._parse_table(schema=True))
4748
4749            wait: t.Optional[bool | exp.Expression] = None
4750            if self._match_text_seq("NOWAIT"):
4751                wait = True
4752            elif self._match_text_seq("WAIT"):
4753                wait = self._parse_primary()
4754            elif self._match_text_seq("SKIP", "LOCKED"):
4755                wait = False
4756
4757            locks.append(
4758                self.expression(exp.Lock, update=update, expressions=expressions, wait=wait)
4759            )
4760
4761        return locks
4762
4763    def parse_set_operation(
4764        self, this: t.Optional[exp.Expression], consume_pipe: bool = False
4765    ) -> t.Optional[exp.Expression]:
4766        start = self._index
4767        _, side_token, kind_token = self._parse_join_parts()
4768
4769        side = side_token.text if side_token else None
4770        kind = kind_token.text if kind_token else None
4771
4772        if not self._match_set(self.SET_OPERATIONS):
4773            self._retreat(start)
4774            return None
4775
4776        token_type = self._prev.token_type
4777
4778        if token_type == TokenType.UNION:
4779            operation: t.Type[exp.SetOperation] = exp.Union
4780        elif token_type == TokenType.EXCEPT:
4781            operation = exp.Except
4782        else:
4783            operation = exp.Intersect
4784
4785        comments = self._prev.comments
4786
4787        if self._match(TokenType.DISTINCT):
4788            distinct: t.Optional[bool] = True
4789        elif self._match(TokenType.ALL):
4790            distinct = False
4791        else:
4792            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4793            if distinct is None:
4794                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4795
4796        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4797            "STRICT", "CORRESPONDING"
4798        )
4799        if self._match_text_seq("CORRESPONDING"):
4800            by_name = True
4801            if not side and not kind:
4802                kind = "INNER"
4803
4804        on_column_list = None
4805        if by_name and self._match_texts(("ON", "BY")):
4806            on_column_list = self._parse_wrapped_csv(self._parse_column)
4807
4808        expression = self._parse_select(
4809            nested=True, parse_set_operation=False, consume_pipe=consume_pipe
4810        )
4811
4812        return self.expression(
4813            operation,
4814            comments=comments,
4815            this=this,
4816            distinct=distinct,
4817            by_name=by_name,
4818            expression=expression,
4819            side=side,
4820            kind=kind,
4821            on=on_column_list,
4822        )
4823
4824    def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4825        while this:
4826            setop = self.parse_set_operation(this)
4827            if not setop:
4828                break
4829            this = setop
4830
4831        if isinstance(this, exp.SetOperation) and self.MODIFIERS_ATTACHED_TO_SET_OP:
4832            expression = this.expression
4833
4834            if expression:
4835                for arg in self.SET_OP_MODIFIERS:
4836                    expr = expression.args.get(arg)
4837                    if expr:
4838                        this.set(arg, expr.pop())
4839
4840        return this
4841
4842    def _parse_expression(self) -> t.Optional[exp.Expression]:
4843        return self._parse_alias(self._parse_assignment())
4844
4845    def _parse_assignment(self) -> t.Optional[exp.Expression]:
4846        this = self._parse_disjunction()
4847        if not this and self._next and self._next.token_type in self.ASSIGNMENT:
4848            # This allows us to parse <non-identifier token> := <expr>
4849            this = exp.column(
4850                t.cast(str, self._advance_any(ignore_reserved=True) and self._prev.text)
4851            )
4852
4853        while self._match_set(self.ASSIGNMENT):
4854            if isinstance(this, exp.Column) and len(this.parts) == 1:
4855                this = this.this
4856
4857            this = self.expression(
4858                self.ASSIGNMENT[self._prev.token_type],
4859                this=this,
4860                comments=self._prev_comments,
4861                expression=self._parse_assignment(),
4862            )
4863
4864        return this
4865
4866    def _parse_disjunction(self) -> t.Optional[exp.Expression]:
4867        return self._parse_tokens(self._parse_conjunction, self.DISJUNCTION)
4868
4869    def _parse_conjunction(self) -> t.Optional[exp.Expression]:
4870        return self._parse_tokens(self._parse_equality, self.CONJUNCTION)
4871
4872    def _parse_equality(self) -> t.Optional[exp.Expression]:
4873        return self._parse_tokens(self._parse_comparison, self.EQUALITY)
4874
4875    def _parse_comparison(self) -> t.Optional[exp.Expression]:
4876        return self._parse_tokens(self._parse_range, self.COMPARISON)
4877
4878    def _parse_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4879        this = this or self._parse_bitwise()
4880        negate = self._match(TokenType.NOT)
4881
4882        if self._match_set(self.RANGE_PARSERS):
4883            expression = self.RANGE_PARSERS[self._prev.token_type](self, this)
4884            if not expression:
4885                return this
4886
4887            this = expression
4888        elif self._match(TokenType.ISNULL):
4889            this = self.expression(exp.Is, this=this, expression=exp.Null())
4890
4891        # Postgres supports ISNULL and NOTNULL for conditions.
4892        # https://blog.andreiavram.ro/postgresql-null-composite-type/
4893        if self._match(TokenType.NOTNULL):
4894            this = self.expression(exp.Is, this=this, expression=exp.Null())
4895            this = self.expression(exp.Not, this=this)
4896
4897        if negate:
4898            this = self._negate_range(this)
4899
4900        if self._match(TokenType.IS):
4901            this = self._parse_is(this)
4902
4903        return this
4904
4905    def _negate_range(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
4906        if not this:
4907            return this
4908
4909        return self.expression(exp.Not, this=this)
4910
4911    def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4912        index = self._index - 1
4913        negate = self._match(TokenType.NOT)
4914
4915        if self._match_text_seq("DISTINCT", "FROM"):
4916            klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ
4917            return self.expression(klass, this=this, expression=self._parse_bitwise())
4918
4919        if self._match(TokenType.JSON):
4920            kind = self._match_texts(self.IS_JSON_PREDICATE_KIND) and self._prev.text.upper()
4921
4922            if self._match_text_seq("WITH"):
4923                _with = True
4924            elif self._match_text_seq("WITHOUT"):
4925                _with = False
4926            else:
4927                _with = None
4928
4929            unique = self._match(TokenType.UNIQUE)
4930            self._match_text_seq("KEYS")
4931            expression: t.Optional[exp.Expression] = self.expression(
4932                exp.JSON, **{"this": kind, "with": _with, "unique": unique}
4933            )
4934        else:
4935            expression = self._parse_primary() or self._parse_null()
4936            if not expression:
4937                self._retreat(index)
4938                return None
4939
4940        this = self.expression(exp.Is, this=this, expression=expression)
4941        return self.expression(exp.Not, this=this) if negate else this
4942
4943    def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In:
4944        unnest = self._parse_unnest(with_alias=False)
4945        if unnest:
4946            this = self.expression(exp.In, this=this, unnest=unnest)
4947        elif self._match_set((TokenType.L_PAREN, TokenType.L_BRACKET)):
4948            matched_l_paren = self._prev.token_type == TokenType.L_PAREN
4949            expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias))
4950
4951            if len(expressions) == 1 and isinstance(expressions[0], exp.Query):
4952                this = self.expression(exp.In, this=this, query=expressions[0].subquery(copy=False))
4953            else:
4954                this = self.expression(exp.In, this=this, expressions=expressions)
4955
4956            if matched_l_paren:
4957                self._match_r_paren(this)
4958            elif not self._match(TokenType.R_BRACKET, expression=this):
4959                self.raise_error("Expecting ]")
4960        else:
4961            this = self.expression(exp.In, this=this, field=self._parse_column())
4962
4963        return this
4964
4965    def _parse_between(self, this: t.Optional[exp.Expression]) -> exp.Between:
4966        low = self._parse_bitwise()
4967        self._match(TokenType.AND)
4968        high = self._parse_bitwise()
4969        return self.expression(exp.Between, this=this, low=low, high=high)
4970
4971    def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
4972        if not self._match(TokenType.ESCAPE):
4973            return this
4974        return self.expression(exp.Escape, this=this, expression=self._parse_string())
4975
4976    def _parse_interval(self, match_interval: bool = True) -> t.Optional[exp.Add | exp.Interval]:
4977        index = self._index
4978
4979        if not self._match(TokenType.INTERVAL) and match_interval:
4980            return None
4981
4982        if self._match(TokenType.STRING, advance=False):
4983            this = self._parse_primary()
4984        else:
4985            this = self._parse_term()
4986
4987        if not this or (
4988            isinstance(this, exp.Column)
4989            and not this.table
4990            and not this.this.quoted
4991            and this.name.upper() == "IS"
4992        ):
4993            self._retreat(index)
4994            return None
4995
4996        unit = self._parse_function() or (
4997            not self._match(TokenType.ALIAS, advance=False)
4998            and self._parse_var(any_token=True, upper=True)
4999        )
5000
5001        # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse
5002        # each INTERVAL expression into this canonical form so it's easy to transpile
5003        if this and this.is_number:
5004            this = exp.Literal.string(this.to_py())
5005        elif this and this.is_string:
5006            parts = exp.INTERVAL_STRING_RE.findall(this.name)
5007            if parts and unit:
5008                # Unconsume the eagerly-parsed unit, since the real unit was part of the string
5009                unit = None
5010                self._retreat(self._index - 1)
5011
5012            if len(parts) == 1:
5013                this = exp.Literal.string(parts[0][0])
5014                unit = self.expression(exp.Var, this=parts[0][1].upper())
5015        if self.INTERVAL_SPANS and self._match_text_seq("TO"):
5016            unit = self.expression(
5017                exp.IntervalSpan, this=unit, expression=self._parse_var(any_token=True, upper=True)
5018            )
5019
5020        interval = self.expression(exp.Interval, this=this, unit=unit)
5021
5022        index = self._index
5023        self._match(TokenType.PLUS)
5024
5025        # Convert INTERVAL 'val_1' unit_1 [+] ... [+] 'val_n' unit_n into a sum of intervals
5026        if self._match_set((TokenType.STRING, TokenType.NUMBER), advance=False):
5027            return self.expression(
5028                exp.Add, this=interval, expression=self._parse_interval(match_interval=False)
5029            )
5030
5031        self._retreat(index)
5032        return interval
5033
5034    def _parse_bitwise(self) -> t.Optional[exp.Expression]:
5035        this = self._parse_term()
5036
5037        while True:
5038            if self._match_set(self.BITWISE):
5039                this = self.expression(
5040                    self.BITWISE[self._prev.token_type],
5041                    this=this,
5042                    expression=self._parse_term(),
5043                )
5044            elif self.dialect.DPIPE_IS_STRING_CONCAT and self._match(TokenType.DPIPE):
5045                this = self.expression(
5046                    exp.DPipe,
5047                    this=this,
5048                    expression=self._parse_term(),
5049                    safe=not self.dialect.STRICT_STRING_CONCAT,
5050                )
5051            elif self._match(TokenType.DQMARK):
5052                this = self.expression(
5053                    exp.Coalesce, this=this, expressions=ensure_list(self._parse_term())
5054                )
5055            elif self._match_pair(TokenType.LT, TokenType.LT):
5056                this = self.expression(
5057                    exp.BitwiseLeftShift, this=this, expression=self._parse_term()
5058                )
5059            elif self._match_pair(TokenType.GT, TokenType.GT):
5060                this = self.expression(
5061                    exp.BitwiseRightShift, this=this, expression=self._parse_term()
5062                )
5063            else:
5064                break
5065
5066        return this
5067
5068    def _parse_term(self) -> t.Optional[exp.Expression]:
5069        this = self._parse_factor()
5070
5071        while self._match_set(self.TERM):
5072            klass = self.TERM[self._prev.token_type]
5073            comments = self._prev_comments
5074            expression = self._parse_factor()
5075
5076            this = self.expression(klass, this=this, comments=comments, expression=expression)
5077
5078            if isinstance(this, exp.Collate):
5079                expr = this.expression
5080
5081                # Preserve collations such as pg_catalog."default" (Postgres) as columns, otherwise
5082                # fallback to Identifier / Var
5083                if isinstance(expr, exp.Column) and len(expr.parts) == 1:
5084                    ident = expr.this
5085                    if isinstance(ident, exp.Identifier):
5086                        this.set("expression", ident if ident.quoted else exp.var(ident.name))
5087
5088        return this
5089
5090    def _parse_factor(self) -> t.Optional[exp.Expression]:
5091        parse_method = self._parse_exponent if self.EXPONENT else self._parse_unary
5092        this = parse_method()
5093
5094        while self._match_set(self.FACTOR):
5095            klass = self.FACTOR[self._prev.token_type]
5096            comments = self._prev_comments
5097            expression = parse_method()
5098
5099            if not expression and klass is exp.IntDiv and self._prev.text.isalpha():
5100                self._retreat(self._index - 1)
5101                return this
5102
5103            this = self.expression(klass, this=this, comments=comments, expression=expression)
5104
5105            if isinstance(this, exp.Div):
5106                this.args["typed"] = self.dialect.TYPED_DIVISION
5107                this.args["safe"] = self.dialect.SAFE_DIVISION
5108
5109        return this
5110
5111    def _parse_exponent(self) -> t.Optional[exp.Expression]:
5112        return self._parse_tokens(self._parse_unary, self.EXPONENT)
5113
5114    def _parse_unary(self) -> t.Optional[exp.Expression]:
5115        if self._match_set(self.UNARY_PARSERS):
5116            return self.UNARY_PARSERS[self._prev.token_type](self)
5117        return self._parse_at_time_zone(self._parse_type())
5118
5119    def _parse_type(
5120        self, parse_interval: bool = True, fallback_to_identifier: bool = False
5121    ) -> t.Optional[exp.Expression]:
5122        interval = parse_interval and self._parse_interval()
5123        if interval:
5124            return interval
5125
5126        index = self._index
5127        data_type = self._parse_types(check_func=True, allow_identifiers=False)
5128
5129        # parse_types() returns a Cast if we parsed BQ's inline constructor <type>(<values>) e.g.
5130        # STRUCT<a INT, b STRING>(1, 'foo'), which is canonicalized to CAST(<values> AS <type>)
5131        if isinstance(data_type, exp.Cast):
5132            # This constructor can contain ops directly after it, for instance struct unnesting:
5133            # STRUCT<a INT, b STRING>(1, 'foo').* --> CAST(STRUCT(1, 'foo') AS STRUCT<a iNT, b STRING).*
5134            return self._parse_column_ops(data_type)
5135
5136        if data_type:
5137            index2 = self._index
5138            this = self._parse_primary()
5139
5140            if isinstance(this, exp.Literal):
5141                literal = this.name
5142                this = self._parse_column_ops(this)
5143
5144                parser = self.TYPE_LITERAL_PARSERS.get(data_type.this)
5145                if parser:
5146                    return parser(self, this, data_type)
5147
5148                if (
5149                    self.ZONE_AWARE_TIMESTAMP_CONSTRUCTOR
5150                    and data_type.is_type(exp.DataType.Type.TIMESTAMP)
5151                    and TIME_ZONE_RE.search(literal)
5152                ):
5153                    data_type = exp.DataType.build("TIMESTAMPTZ")
5154
5155                return self.expression(exp.Cast, this=this, to=data_type)
5156
5157            # The expressions arg gets set by the parser when we have something like DECIMAL(38, 0)
5158            # in the input SQL. In that case, we'll produce these tokens: DECIMAL ( 38 , 0 )
5159            #
5160            # If the index difference here is greater than 1, that means the parser itself must have
5161            # consumed additional tokens such as the DECIMAL scale and precision in the above example.
5162            #
5163            # If it's not greater than 1, then it must be 1, because we've consumed at least the type
5164            # keyword, meaning that the expressions arg of the DataType must have gotten set by a
5165            # callable in the TYPE_CONVERTERS mapping. For example, Snowflake converts DECIMAL to
5166            # DECIMAL(38, 0)) in order to facilitate the data type's transpilation.
5167            #
5168            # In these cases, we don't really want to return the converted type, but instead retreat
5169            # and try to parse a Column or Identifier in the section below.
5170            if data_type.expressions and index2 - index > 1:
5171                self._retreat(index2)
5172                return self._parse_column_ops(data_type)
5173
5174            self._retreat(index)
5175
5176        if fallback_to_identifier:
5177            return self._parse_id_var()
5178
5179        this = self._parse_column()
5180        return this and self._parse_column_ops(this)
5181
5182    def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]:
5183        this = self._parse_type()
5184        if not this:
5185            return None
5186
5187        if isinstance(this, exp.Column) and not this.table:
5188            this = exp.var(this.name.upper())
5189
5190        return self.expression(
5191            exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True)
5192        )
5193
5194    def _parse_user_defined_type(self, identifier: exp.Identifier) -> t.Optional[exp.Expression]:
5195        type_name = identifier.name
5196
5197        while self._match(TokenType.DOT):
5198            type_name = f"{type_name}.{self._advance_any() and self._prev.text}"
5199
5200        return exp.DataType.build(type_name, udt=True)
5201
5202    def _parse_types(
5203        self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True
5204    ) -> t.Optional[exp.Expression]:
5205        index = self._index
5206
5207        this: t.Optional[exp.Expression] = None
5208        prefix = self._match_text_seq("SYSUDTLIB", ".")
5209
5210        if not self._match_set(self.TYPE_TOKENS):
5211            identifier = allow_identifiers and self._parse_id_var(
5212                any_token=False, tokens=(TokenType.VAR,)
5213            )
5214            if isinstance(identifier, exp.Identifier):
5215                tokens = self.dialect.tokenize(identifier.sql(dialect=self.dialect))
5216
5217                if len(tokens) != 1:
5218                    self.raise_error("Unexpected identifier", self._prev)
5219
5220                if tokens[0].token_type in self.TYPE_TOKENS:
5221                    self._prev = tokens[0]
5222                elif self.dialect.SUPPORTS_USER_DEFINED_TYPES:
5223                    this = self._parse_user_defined_type(identifier)
5224                else:
5225                    self._retreat(self._index - 1)
5226                    return None
5227            else:
5228                return None
5229
5230        type_token = self._prev.token_type
5231
5232        if type_token == TokenType.PSEUDO_TYPE:
5233            return self.expression(exp.PseudoType, this=self._prev.text.upper())
5234
5235        if type_token == TokenType.OBJECT_IDENTIFIER:
5236            return self.expression(exp.ObjectIdentifier, this=self._prev.text.upper())
5237
5238        # https://materialize.com/docs/sql/types/map/
5239        if type_token == TokenType.MAP and self._match(TokenType.L_BRACKET):
5240            key_type = self._parse_types(
5241                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5242            )
5243            if not self._match(TokenType.FARROW):
5244                self._retreat(index)
5245                return None
5246
5247            value_type = self._parse_types(
5248                check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5249            )
5250            if not self._match(TokenType.R_BRACKET):
5251                self._retreat(index)
5252                return None
5253
5254            return exp.DataType(
5255                this=exp.DataType.Type.MAP,
5256                expressions=[key_type, value_type],
5257                nested=True,
5258                prefix=prefix,
5259            )
5260
5261        nested = type_token in self.NESTED_TYPE_TOKENS
5262        is_struct = type_token in self.STRUCT_TYPE_TOKENS
5263        is_aggregate = type_token in self.AGGREGATE_TYPE_TOKENS
5264        expressions = None
5265        maybe_func = False
5266
5267        if self._match(TokenType.L_PAREN):
5268            if is_struct:
5269                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5270            elif nested:
5271                expressions = self._parse_csv(
5272                    lambda: self._parse_types(
5273                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5274                    )
5275                )
5276                if type_token == TokenType.NULLABLE and len(expressions) == 1:
5277                    this = expressions[0]
5278                    this.set("nullable", True)
5279                    self._match_r_paren()
5280                    return this
5281            elif type_token in self.ENUM_TYPE_TOKENS:
5282                expressions = self._parse_csv(self._parse_equality)
5283            elif is_aggregate:
5284                func_or_ident = self._parse_function(anonymous=True) or self._parse_id_var(
5285                    any_token=False, tokens=(TokenType.VAR, TokenType.ANY)
5286                )
5287                if not func_or_ident:
5288                    return None
5289                expressions = [func_or_ident]
5290                if self._match(TokenType.COMMA):
5291                    expressions.extend(
5292                        self._parse_csv(
5293                            lambda: self._parse_types(
5294                                check_func=check_func,
5295                                schema=schema,
5296                                allow_identifiers=allow_identifiers,
5297                            )
5298                        )
5299                    )
5300            else:
5301                expressions = self._parse_csv(self._parse_type_size)
5302
5303                # https://docs.snowflake.com/en/sql-reference/data-types-vector
5304                if type_token == TokenType.VECTOR and len(expressions) == 2:
5305                    expressions[0] = exp.DataType.build(expressions[0].name, dialect=self.dialect)
5306
5307            if not expressions or not self._match(TokenType.R_PAREN):
5308                self._retreat(index)
5309                return None
5310
5311            maybe_func = True
5312
5313        values: t.Optional[t.List[exp.Expression]] = None
5314
5315        if nested and self._match(TokenType.LT):
5316            if is_struct:
5317                expressions = self._parse_csv(lambda: self._parse_struct_types(type_required=True))
5318            else:
5319                expressions = self._parse_csv(
5320                    lambda: self._parse_types(
5321                        check_func=check_func, schema=schema, allow_identifiers=allow_identifiers
5322                    )
5323                )
5324
5325            if not self._match(TokenType.GT):
5326                self.raise_error("Expecting >")
5327
5328            if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)):
5329                values = self._parse_csv(self._parse_assignment)
5330                if not values and is_struct:
5331                    values = None
5332                    self._retreat(self._index - 1)
5333                else:
5334                    self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN))
5335
5336        if type_token in self.TIMESTAMPS:
5337            if self._match_text_seq("WITH", "TIME", "ZONE"):
5338                maybe_func = False
5339                tz_type = (
5340                    exp.DataType.Type.TIMETZ
5341                    if type_token in self.TIMES
5342                    else exp.DataType.Type.TIMESTAMPTZ
5343                )
5344                this = exp.DataType(this=tz_type, expressions=expressions)
5345            elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"):
5346                maybe_func = False
5347                this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions)
5348            elif self._match_text_seq("WITHOUT", "TIME", "ZONE"):
5349                maybe_func = False
5350        elif type_token == TokenType.INTERVAL:
5351            unit = self._parse_var(upper=True)
5352            if unit:
5353                if self._match_text_seq("TO"):
5354                    unit = exp.IntervalSpan(this=unit, expression=self._parse_var(upper=True))
5355
5356                this = self.expression(exp.DataType, this=self.expression(exp.Interval, unit=unit))
5357            else:
5358                this = self.expression(exp.DataType, this=exp.DataType.Type.INTERVAL)
5359        elif type_token == TokenType.VOID:
5360            this = exp.DataType(this=exp.DataType.Type.NULL)
5361
5362        if maybe_func and check_func:
5363            index2 = self._index
5364            peek = self._parse_string()
5365
5366            if not peek:
5367                self._retreat(index)
5368                return None
5369
5370            self._retreat(index2)
5371
5372        if not this:
5373            if self._match_text_seq("UNSIGNED"):
5374                unsigned_type_token = self.SIGNED_TO_UNSIGNED_TYPE_TOKEN.get(type_token)
5375                if not unsigned_type_token:
5376                    self.raise_error(f"Cannot convert {type_token.value} to unsigned.")
5377
5378                type_token = unsigned_type_token or type_token
5379
5380            this = exp.DataType(
5381                this=exp.DataType.Type[type_token.value],
5382                expressions=expressions,
5383                nested=nested,
5384                prefix=prefix,
5385            )
5386
5387            # Empty arrays/structs are allowed
5388            if values is not None:
5389                cls = exp.Struct if is_struct else exp.Array
5390                this = exp.cast(cls(expressions=values), this, copy=False)
5391
5392        elif expressions:
5393            this.set("expressions", expressions)
5394
5395        # https://materialize.com/docs/sql/types/list/#type-name
5396        while self._match(TokenType.LIST):
5397            this = exp.DataType(this=exp.DataType.Type.LIST, expressions=[this], nested=True)
5398
5399        index = self._index
5400
5401        # Postgres supports the INT ARRAY[3] syntax as a synonym for INT[3]
5402        matched_array = self._match(TokenType.ARRAY)
5403
5404        while self._curr:
5405            datatype_token = self._prev.token_type
5406            matched_l_bracket = self._match(TokenType.L_BRACKET)
5407
5408            if (not matched_l_bracket and not matched_array) or (
5409                datatype_token == TokenType.ARRAY and self._match(TokenType.R_BRACKET)
5410            ):
5411                # Postgres allows casting empty arrays such as ARRAY[]::INT[],
5412                # not to be confused with the fixed size array parsing
5413                break
5414
5415            matched_array = False
5416            values = self._parse_csv(self._parse_assignment) or None
5417            if (
5418                values
5419                and not schema
5420                and (
5421                    not self.dialect.SUPPORTS_FIXED_SIZE_ARRAYS or datatype_token == TokenType.ARRAY
5422                )
5423            ):
5424                # Retreating here means that we should not parse the following values as part of the data type, e.g. in DuckDB
5425                # 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
5426                self._retreat(index)
5427                break
5428
5429            this = exp.DataType(
5430                this=exp.DataType.Type.ARRAY, expressions=[this], values=values, nested=True
5431            )
5432            self._match(TokenType.R_BRACKET)
5433
5434        if self.TYPE_CONVERTERS and isinstance(this.this, exp.DataType.Type):
5435            converter = self.TYPE_CONVERTERS.get(this.this)
5436            if converter:
5437                this = converter(t.cast(exp.DataType, this))
5438
5439        return this
5440
5441    def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]:
5442        index = self._index
5443
5444        if (
5445            self._curr
5446            and self._next
5447            and self._curr.token_type in self.TYPE_TOKENS
5448            and self._next.token_type in self.TYPE_TOKENS
5449        ):
5450            # Takes care of special cases like `STRUCT<list ARRAY<...>>` where the identifier is also a
5451            # type token. Without this, the list will be parsed as a type and we'll eventually crash
5452            this = self._parse_id_var()
5453        else:
5454            this = (
5455                self._parse_type(parse_interval=False, fallback_to_identifier=True)
5456                or self._parse_id_var()
5457            )
5458
5459        self._match(TokenType.COLON)
5460
5461        if (
5462            type_required
5463            and not isinstance(this, exp.DataType)
5464            and not self._match_set(self.TYPE_TOKENS, advance=False)
5465        ):
5466            self._retreat(index)
5467            return self._parse_types()
5468
5469        return self._parse_column_def(this)
5470
5471    def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5472        if not self._match_text_seq("AT", "TIME", "ZONE"):
5473            return this
5474        return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary())
5475
5476    def _parse_column(self) -> t.Optional[exp.Expression]:
5477        this = self._parse_column_reference()
5478        column = self._parse_column_ops(this) if this else self._parse_bracket(this)
5479
5480        if self.dialect.SUPPORTS_COLUMN_JOIN_MARKS and column:
5481            column.set("join_mark", self._match(TokenType.JOIN_MARKER))
5482
5483        return column
5484
5485    def _parse_column_reference(self) -> t.Optional[exp.Expression]:
5486        this = self._parse_field()
5487        if (
5488            not this
5489            and self._match(TokenType.VALUES, advance=False)
5490            and self.VALUES_FOLLOWED_BY_PAREN
5491            and (not self._next or self._next.token_type != TokenType.L_PAREN)
5492        ):
5493            this = self._parse_id_var()
5494
5495        if isinstance(this, exp.Identifier):
5496            # We bubble up comments from the Identifier to the Column
5497            this = self.expression(exp.Column, comments=this.pop_comments(), this=this)
5498
5499        return this
5500
5501    def _parse_colon_as_variant_extract(
5502        self, this: t.Optional[exp.Expression]
5503    ) -> t.Optional[exp.Expression]:
5504        casts = []
5505        json_path = []
5506        escape = None
5507
5508        while self._match(TokenType.COLON):
5509            start_index = self._index
5510
5511            # Snowflake allows reserved keywords as json keys but advance_any() excludes TokenType.SELECT from any_tokens=True
5512            path = self._parse_column_ops(
5513                self._parse_field(any_token=True, tokens=(TokenType.SELECT,))
5514            )
5515
5516            # The cast :: operator has a lower precedence than the extraction operator :, so
5517            # we rearrange the AST appropriately to avoid casting the JSON path
5518            while isinstance(path, exp.Cast):
5519                casts.append(path.to)
5520                path = path.this
5521
5522            if casts:
5523                dcolon_offset = next(
5524                    i
5525                    for i, t in enumerate(self._tokens[start_index:])
5526                    if t.token_type == TokenType.DCOLON
5527                )
5528                end_token = self._tokens[start_index + dcolon_offset - 1]
5529            else:
5530                end_token = self._prev
5531
5532            if path:
5533                # Escape single quotes from Snowflake's colon extraction (e.g. col:"a'b") as
5534                # it'll roundtrip to a string literal in GET_PATH
5535                if isinstance(path, exp.Identifier) and path.quoted:
5536                    escape = True
5537
5538                json_path.append(self._find_sql(self._tokens[start_index], end_token))
5539
5540        # The VARIANT extract in Snowflake/Databricks is parsed as a JSONExtract; Snowflake uses the json_path in GET_PATH() while
5541        # Databricks transforms it back to the colon/dot notation
5542        if json_path:
5543            json_path_expr = self.dialect.to_json_path(exp.Literal.string(".".join(json_path)))
5544
5545            if json_path_expr:
5546                json_path_expr.set("escape", escape)
5547
5548            this = self.expression(
5549                exp.JSONExtract,
5550                this=this,
5551                expression=json_path_expr,
5552                variant_extract=True,
5553            )
5554
5555            while casts:
5556                this = self.expression(exp.Cast, this=this, to=casts.pop())
5557
5558        return this
5559
5560    def _parse_dcolon(self) -> t.Optional[exp.Expression]:
5561        return self._parse_types()
5562
5563    def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
5564        this = self._parse_bracket(this)
5565
5566        while self._match_set(self.COLUMN_OPERATORS):
5567            op_token = self._prev.token_type
5568            op = self.COLUMN_OPERATORS.get(op_token)
5569
5570            if op_token in (TokenType.DCOLON, TokenType.DOTCOLON):
5571                field = self._parse_dcolon()
5572                if not field:
5573                    self.raise_error("Expected type")
5574            elif op and self._curr:
5575                field = self._parse_column_reference() or self._parse_bracket()
5576                if isinstance(field, exp.Column) and self._match(TokenType.DOT, advance=False):
5577                    field = self._parse_column_ops(field)
5578            else:
5579                field = self._parse_field(any_token=True, anonymous_func=True)
5580
5581            # Function calls can be qualified, e.g., x.y.FOO()
5582            # This converts the final AST to a series of Dots leading to the function call
5583            # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules
5584            if isinstance(field, (exp.Func, exp.Window)) and this:
5585                this = this.transform(
5586                    lambda n: n.to_dot(include_dots=False) if isinstance(n, exp.Column) else n
5587                )
5588
5589            if op:
5590                this = op(self, this, field)
5591            elif isinstance(this, exp.Column) and not this.args.get("catalog"):
5592                this = self.expression(
5593                    exp.Column,
5594                    comments=this.comments,
5595                    this=field,
5596                    table=this.this,
5597                    db=this.args.get("table"),
5598                    catalog=this.args.get("db"),
5599                )
5600            elif isinstance(field, exp.Window):
5601                # Move the exp.Dot's to the window's function
5602                window_func = self.expression(exp.Dot, this=this, expression=field.this)
5603                field.set("this", window_func)
5604                this = field
5605            else:
5606                this = self.expression(exp.Dot, this=this, expression=field)
5607
5608            if field and field.comments:
5609                t.cast(exp.Expression, this).add_comments(field.pop_comments())
5610
5611            this = self._parse_bracket(this)
5612
5613        return self._parse_colon_as_variant_extract(this) if self.COLON_IS_VARIANT_EXTRACT else this
5614
5615    def _parse_paren(self) -> t.Optional[exp.Expression]:
5616        if not self._match(TokenType.L_PAREN):
5617            return None
5618
5619        comments = self._prev_comments
5620        query = self._parse_select()
5621
5622        if query:
5623            expressions = [query]
5624        else:
5625            expressions = self._parse_expressions()
5626
5627        this = self._parse_query_modifiers(seq_get(expressions, 0))
5628
5629        if not this and self._match(TokenType.R_PAREN, advance=False):
5630            this = self.expression(exp.Tuple)
5631        elif isinstance(this, exp.UNWRAPPED_QUERIES):
5632            this = self._parse_subquery(this=this, parse_alias=False)
5633        elif isinstance(this, exp.Subquery):
5634            this = self._parse_subquery(this=self._parse_set_operations(this), parse_alias=False)
5635        elif len(expressions) > 1 or self._prev.token_type == TokenType.COMMA:
5636            this = self.expression(exp.Tuple, expressions=expressions)
5637        else:
5638            this = self.expression(exp.Paren, this=this)
5639
5640        if this:
5641            this.add_comments(comments)
5642
5643        self._match_r_paren(expression=this)
5644        return this
5645
5646    def _parse_primary(self) -> t.Optional[exp.Expression]:
5647        if self._match_set(self.PRIMARY_PARSERS):
5648            token_type = self._prev.token_type
5649            primary = self.PRIMARY_PARSERS[token_type](self, self._prev)
5650
5651            if token_type == TokenType.STRING:
5652                expressions = [primary]
5653                while self._match(TokenType.STRING):
5654                    expressions.append(exp.Literal.string(self._prev.text))
5655
5656                if len(expressions) > 1:
5657                    return self.expression(exp.Concat, expressions=expressions)
5658
5659            return primary
5660
5661        if self._match_pair(TokenType.DOT, TokenType.NUMBER):
5662            return exp.Literal.number(f"0.{self._prev.text}")
5663
5664        return self._parse_paren()
5665
5666    def _parse_field(
5667        self,
5668        any_token: bool = False,
5669        tokens: t.Optional[t.Collection[TokenType]] = None,
5670        anonymous_func: bool = False,
5671    ) -> t.Optional[exp.Expression]:
5672        if anonymous_func:
5673            field = (
5674                self._parse_function(anonymous=anonymous_func, any_token=any_token)
5675                or self._parse_primary()
5676            )
5677        else:
5678            field = self._parse_primary() or self._parse_function(
5679                anonymous=anonymous_func, any_token=any_token
5680            )
5681        return field or self._parse_id_var(any_token=any_token, tokens=tokens)
5682
5683    def _parse_function(
5684        self,
5685        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5686        anonymous: bool = False,
5687        optional_parens: bool = True,
5688        any_token: bool = False,
5689    ) -> t.Optional[exp.Expression]:
5690        # This allows us to also parse {fn <function>} syntax (Snowflake, MySQL support this)
5691        # See: https://community.snowflake.com/s/article/SQL-Escape-Sequences
5692        fn_syntax = False
5693        if (
5694            self._match(TokenType.L_BRACE, advance=False)
5695            and self._next
5696            and self._next.text.upper() == "FN"
5697        ):
5698            self._advance(2)
5699            fn_syntax = True
5700
5701        func = self._parse_function_call(
5702            functions=functions,
5703            anonymous=anonymous,
5704            optional_parens=optional_parens,
5705            any_token=any_token,
5706        )
5707
5708        if fn_syntax:
5709            self._match(TokenType.R_BRACE)
5710
5711        return func
5712
5713    def _parse_function_call(
5714        self,
5715        functions: t.Optional[t.Dict[str, t.Callable]] = None,
5716        anonymous: bool = False,
5717        optional_parens: bool = True,
5718        any_token: bool = False,
5719    ) -> t.Optional[exp.Expression]:
5720        if not self._curr:
5721            return None
5722
5723        comments = self._curr.comments
5724        token = self._curr
5725        token_type = self._curr.token_type
5726        this = self._curr.text
5727        upper = this.upper()
5728
5729        parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper)
5730        if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS:
5731            self._advance()
5732            return self._parse_window(parser(self))
5733
5734        if not self._next or self._next.token_type != TokenType.L_PAREN:
5735            if optional_parens and token_type in self.NO_PAREN_FUNCTIONS:
5736                self._advance()
5737                return self.expression(self.NO_PAREN_FUNCTIONS[token_type])
5738
5739            return None
5740
5741        if any_token:
5742            if token_type in self.RESERVED_TOKENS:
5743                return None
5744        elif token_type not in self.FUNC_TOKENS:
5745            return None
5746
5747        self._advance(2)
5748
5749        parser = self.FUNCTION_PARSERS.get(upper)
5750        if parser and not anonymous:
5751            this = parser(self)
5752        else:
5753            subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type)
5754
5755            if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH):
5756                this = self.expression(
5757                    subquery_predicate, comments=comments, this=self._parse_select()
5758                )
5759                self._match_r_paren()
5760                return this
5761
5762            if functions is None:
5763                functions = self.FUNCTIONS
5764
5765            function = functions.get(upper)
5766            known_function = function and not anonymous
5767
5768            alias = not known_function or upper in self.FUNCTIONS_WITH_ALIASED_ARGS
5769            args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
5770
5771            post_func_comments = self._curr and self._curr.comments
5772            if known_function and post_func_comments:
5773                # If the user-inputted comment "/* sqlglot.anonymous */" is following the function
5774                # call we'll construct it as exp.Anonymous, even if it's "known"
5775                if any(
5776                    comment.lstrip().startswith(exp.SQLGLOT_ANONYMOUS)
5777                    for comment in post_func_comments
5778                ):
5779                    known_function = False
5780
5781            if alias and known_function:
5782                args = self._kv_to_prop_eq(args)
5783
5784            if known_function:
5785                func_builder = t.cast(t.Callable, function)
5786
5787                if "dialect" in func_builder.__code__.co_varnames:
5788                    func = func_builder(args, dialect=self.dialect)
5789                else:
5790                    func = func_builder(args)
5791
5792                func = self.validate_expression(func, args)
5793                if self.dialect.PRESERVE_ORIGINAL_NAMES:
5794                    func.meta["name"] = this
5795
5796                this = func
5797            else:
5798                if token_type == TokenType.IDENTIFIER:
5799                    this = exp.Identifier(this=this, quoted=True).update_positions(token)
5800
5801                this = self.expression(exp.Anonymous, this=this, expressions=args)
5802                this = this.update_positions(token)
5803
5804        if isinstance(this, exp.Expression):
5805            this.add_comments(comments)
5806
5807        self._match_r_paren(this)
5808        return self._parse_window(this)
5809
5810    def _to_prop_eq(self, expression: exp.Expression, index: int) -> exp.Expression:
5811        return expression
5812
5813    def _kv_to_prop_eq(self, expressions: t.List[exp.Expression]) -> t.List[exp.Expression]:
5814        transformed = []
5815
5816        for index, e in enumerate(expressions):
5817            if isinstance(e, self.KEY_VALUE_DEFINITIONS):
5818                if isinstance(e, exp.Alias):
5819                    e = self.expression(exp.PropertyEQ, this=e.args.get("alias"), expression=e.this)
5820
5821                if not isinstance(e, exp.PropertyEQ):
5822                    e = self.expression(
5823                        exp.PropertyEQ, this=exp.to_identifier(e.this.name), expression=e.expression
5824                    )
5825
5826                if isinstance(e.this, exp.Column):
5827                    e.this.replace(e.this.this)
5828            else:
5829                e = self._to_prop_eq(e, index)
5830
5831            transformed.append(e)
5832
5833        return transformed
5834
5835    def _parse_user_defined_function_expression(self) -> t.Optional[exp.Expression]:
5836        return self._parse_statement()
5837
5838    def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
5839        return self._parse_column_def(this=self._parse_id_var(), computed_column=False)
5840
5841    def _parse_user_defined_function(
5842        self, kind: t.Optional[TokenType] = None
5843    ) -> t.Optional[exp.Expression]:
5844        this = self._parse_table_parts(schema=True)
5845
5846        if not self._match(TokenType.L_PAREN):
5847            return this
5848
5849        expressions = self._parse_csv(self._parse_function_parameter)
5850        self._match_r_paren()
5851        return self.expression(
5852            exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True
5853        )
5854
5855    def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier:
5856        literal = self._parse_primary()
5857        if literal:
5858            return self.expression(exp.Introducer, this=token.text, expression=literal)
5859
5860        return self._identifier_expression(token)
5861
5862    def _parse_session_parameter(self) -> exp.SessionParameter:
5863        kind = None
5864        this = self._parse_id_var() or self._parse_primary()
5865
5866        if this and self._match(TokenType.DOT):
5867            kind = this.name
5868            this = self._parse_var() or self._parse_primary()
5869
5870        return self.expression(exp.SessionParameter, this=this, kind=kind)
5871
5872    def _parse_lambda_arg(self) -> t.Optional[exp.Expression]:
5873        return self._parse_id_var()
5874
5875    def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]:
5876        index = self._index
5877
5878        if self._match(TokenType.L_PAREN):
5879            expressions = t.cast(
5880                t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_lambda_arg)
5881            )
5882
5883            if not self._match(TokenType.R_PAREN):
5884                self._retreat(index)
5885        else:
5886            expressions = [self._parse_lambda_arg()]
5887
5888        if self._match_set(self.LAMBDAS):
5889            return self.LAMBDAS[self._prev.token_type](self, expressions)
5890
5891        self._retreat(index)
5892
5893        this: t.Optional[exp.Expression]
5894
5895        if self._match(TokenType.DISTINCT):
5896            this = self.expression(
5897                exp.Distinct, expressions=self._parse_csv(self._parse_assignment)
5898            )
5899        else:
5900            this = self._parse_select_or_expression(alias=alias)
5901
5902        return self._parse_limit(
5903            self._parse_order(self._parse_having_max(self._parse_respect_or_ignore_nulls(this)))
5904        )
5905
5906    def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
5907        index = self._index
5908        if not self._match(TokenType.L_PAREN):
5909            return this
5910
5911        # Disambiguate between schema and subquery/CTE, e.g. in INSERT INTO table (<expr>),
5912        # expr can be of both types
5913        if self._match_set(self.SELECT_START_TOKENS):
5914            self._retreat(index)
5915            return this
5916        args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def())
5917        self._match_r_paren()
5918        return self.expression(exp.Schema, this=this, expressions=args)
5919
5920    def _parse_field_def(self) -> t.Optional[exp.Expression]:
5921        return self._parse_column_def(self._parse_field(any_token=True))
5922
5923    def _parse_column_def(
5924        self, this: t.Optional[exp.Expression], computed_column: bool = True
5925    ) -> t.Optional[exp.Expression]:
5926        # column defs are not really columns, they're identifiers
5927        if isinstance(this, exp.Column):
5928            this = this.this
5929
5930        if not computed_column:
5931            self._match(TokenType.ALIAS)
5932
5933        kind = self._parse_types(schema=True)
5934
5935        if self._match_text_seq("FOR", "ORDINALITY"):
5936            return self.expression(exp.ColumnDef, this=this, ordinality=True)
5937
5938        constraints: t.List[exp.Expression] = []
5939
5940        if (not kind and self._match(TokenType.ALIAS)) or self._match_texts(
5941            ("ALIAS", "MATERIALIZED")
5942        ):
5943            persisted = self._prev.text.upper() == "MATERIALIZED"
5944            constraint_kind = exp.ComputedColumnConstraint(
5945                this=self._parse_assignment(),
5946                persisted=persisted or self._match_text_seq("PERSISTED"),
5947                not_null=self._match_pair(TokenType.NOT, TokenType.NULL),
5948            )
5949            constraints.append(self.expression(exp.ColumnConstraint, kind=constraint_kind))
5950        elif (
5951            kind
5952            and self._match(TokenType.ALIAS, advance=False)
5953            and (
5954                not self.WRAPPED_TRANSFORM_COLUMN_CONSTRAINT
5955                or (self._next and self._next.token_type == TokenType.L_PAREN)
5956            )
5957        ):
5958            self._advance()
5959            constraints.append(
5960                self.expression(
5961                    exp.ColumnConstraint,
5962                    kind=exp.ComputedColumnConstraint(
5963                        this=self._parse_disjunction(),
5964                        persisted=self._match_texts(("STORED", "VIRTUAL"))
5965                        and self._prev.text.upper() == "STORED",
5966                    ),
5967                )
5968            )
5969
5970        while True:
5971            constraint = self._parse_column_constraint()
5972            if not constraint:
5973                break
5974            constraints.append(constraint)
5975
5976        if not kind and not constraints:
5977            return this
5978
5979        return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints)
5980
5981    def _parse_auto_increment(
5982        self,
5983    ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint:
5984        start = None
5985        increment = None
5986        order = None
5987
5988        if self._match(TokenType.L_PAREN, advance=False):
5989            args = self._parse_wrapped_csv(self._parse_bitwise)
5990            start = seq_get(args, 0)
5991            increment = seq_get(args, 1)
5992        elif self._match_text_seq("START"):
5993            start = self._parse_bitwise()
5994            self._match_text_seq("INCREMENT")
5995            increment = self._parse_bitwise()
5996            if self._match_text_seq("ORDER"):
5997                order = True
5998            elif self._match_text_seq("NOORDER"):
5999                order = False
6000
6001        if start and increment:
6002            return exp.GeneratedAsIdentityColumnConstraint(
6003                start=start, increment=increment, this=False, order=order
6004            )
6005
6006        return exp.AutoIncrementColumnConstraint()
6007
6008    def _parse_auto_property(self) -> t.Optional[exp.AutoRefreshProperty]:
6009        if not self._match_text_seq("REFRESH"):
6010            self._retreat(self._index - 1)
6011            return None
6012        return self.expression(exp.AutoRefreshProperty, this=self._parse_var(upper=True))
6013
6014    def _parse_compress(self) -> exp.CompressColumnConstraint:
6015        if self._match(TokenType.L_PAREN, advance=False):
6016            return self.expression(
6017                exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise)
6018            )
6019
6020        return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise())
6021
6022    def _parse_generated_as_identity(
6023        self,
6024    ) -> (
6025        exp.GeneratedAsIdentityColumnConstraint
6026        | exp.ComputedColumnConstraint
6027        | exp.GeneratedAsRowColumnConstraint
6028    ):
6029        if self._match_text_seq("BY", "DEFAULT"):
6030            on_null = self._match_pair(TokenType.ON, TokenType.NULL)
6031            this = self.expression(
6032                exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null
6033            )
6034        else:
6035            self._match_text_seq("ALWAYS")
6036            this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True)
6037
6038        self._match(TokenType.ALIAS)
6039
6040        if self._match_text_seq("ROW"):
6041            start = self._match_text_seq("START")
6042            if not start:
6043                self._match(TokenType.END)
6044            hidden = self._match_text_seq("HIDDEN")
6045            return self.expression(exp.GeneratedAsRowColumnConstraint, start=start, hidden=hidden)
6046
6047        identity = self._match_text_seq("IDENTITY")
6048
6049        if self._match(TokenType.L_PAREN):
6050            if self._match(TokenType.START_WITH):
6051                this.set("start", self._parse_bitwise())
6052            if self._match_text_seq("INCREMENT", "BY"):
6053                this.set("increment", self._parse_bitwise())
6054            if self._match_text_seq("MINVALUE"):
6055                this.set("minvalue", self._parse_bitwise())
6056            if self._match_text_seq("MAXVALUE"):
6057                this.set("maxvalue", self._parse_bitwise())
6058
6059            if self._match_text_seq("CYCLE"):
6060                this.set("cycle", True)
6061            elif self._match_text_seq("NO", "CYCLE"):
6062                this.set("cycle", False)
6063
6064            if not identity:
6065                this.set("expression", self._parse_range())
6066            elif not this.args.get("start") and self._match(TokenType.NUMBER, advance=False):
6067                args = self._parse_csv(self._parse_bitwise)
6068                this.set("start", seq_get(args, 0))
6069                this.set("increment", seq_get(args, 1))
6070
6071            self._match_r_paren()
6072
6073        return this
6074
6075    def _parse_inline(self) -> exp.InlineLengthColumnConstraint:
6076        self._match_text_seq("LENGTH")
6077        return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise())
6078
6079    def _parse_not_constraint(self) -> t.Optional[exp.Expression]:
6080        if self._match_text_seq("NULL"):
6081            return self.expression(exp.NotNullColumnConstraint)
6082        if self._match_text_seq("CASESPECIFIC"):
6083            return self.expression(exp.CaseSpecificColumnConstraint, not_=True)
6084        if self._match_text_seq("FOR", "REPLICATION"):
6085            return self.expression(exp.NotForReplicationColumnConstraint)
6086
6087        # Unconsume the `NOT` token
6088        self._retreat(self._index - 1)
6089        return None
6090
6091    def _parse_column_constraint(self) -> t.Optional[exp.Expression]:
6092        this = self._match(TokenType.CONSTRAINT) and self._parse_id_var()
6093
6094        procedure_option_follows = (
6095            self._match(TokenType.WITH, advance=False)
6096            and self._next
6097            and self._next.text.upper() in self.PROCEDURE_OPTIONS
6098        )
6099
6100        if not procedure_option_follows and self._match_texts(self.CONSTRAINT_PARSERS):
6101            return self.expression(
6102                exp.ColumnConstraint,
6103                this=this,
6104                kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self),
6105            )
6106
6107        return this
6108
6109    def _parse_constraint(self) -> t.Optional[exp.Expression]:
6110        if not self._match(TokenType.CONSTRAINT):
6111            return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS)
6112
6113        return self.expression(
6114            exp.Constraint,
6115            this=self._parse_id_var(),
6116            expressions=self._parse_unnamed_constraints(),
6117        )
6118
6119    def _parse_unnamed_constraints(self) -> t.List[exp.Expression]:
6120        constraints = []
6121        while True:
6122            constraint = self._parse_unnamed_constraint() or self._parse_function()
6123            if not constraint:
6124                break
6125            constraints.append(constraint)
6126
6127        return constraints
6128
6129    def _parse_unnamed_constraint(
6130        self, constraints: t.Optional[t.Collection[str]] = None
6131    ) -> t.Optional[exp.Expression]:
6132        if self._match(TokenType.IDENTIFIER, advance=False) or not self._match_texts(
6133            constraints or self.CONSTRAINT_PARSERS
6134        ):
6135            return None
6136
6137        constraint = self._prev.text.upper()
6138        if constraint not in self.CONSTRAINT_PARSERS:
6139            self.raise_error(f"No parser found for schema constraint {constraint}.")
6140
6141        return self.CONSTRAINT_PARSERS[constraint](self)
6142
6143    def _parse_unique_key(self) -> t.Optional[exp.Expression]:
6144        return self._parse_id_var(any_token=False)
6145
6146    def _parse_unique(self) -> exp.UniqueColumnConstraint:
6147        self._match_text_seq("KEY")
6148        return self.expression(
6149            exp.UniqueColumnConstraint,
6150            nulls=self._match_text_seq("NULLS", "NOT", "DISTINCT"),
6151            this=self._parse_schema(self._parse_unique_key()),
6152            index_type=self._match(TokenType.USING) and self._advance_any() and self._prev.text,
6153            on_conflict=self._parse_on_conflict(),
6154            options=self._parse_key_constraint_options(),
6155        )
6156
6157    def _parse_key_constraint_options(self) -> t.List[str]:
6158        options = []
6159        while True:
6160            if not self._curr:
6161                break
6162
6163            if self._match(TokenType.ON):
6164                action = None
6165                on = self._advance_any() and self._prev.text
6166
6167                if self._match_text_seq("NO", "ACTION"):
6168                    action = "NO ACTION"
6169                elif self._match_text_seq("CASCADE"):
6170                    action = "CASCADE"
6171                elif self._match_text_seq("RESTRICT"):
6172                    action = "RESTRICT"
6173                elif self._match_pair(TokenType.SET, TokenType.NULL):
6174                    action = "SET NULL"
6175                elif self._match_pair(TokenType.SET, TokenType.DEFAULT):
6176                    action = "SET DEFAULT"
6177                else:
6178                    self.raise_error("Invalid key constraint")
6179
6180                options.append(f"ON {on} {action}")
6181            else:
6182                var = self._parse_var_from_options(
6183                    self.KEY_CONSTRAINT_OPTIONS, raise_unmatched=False
6184                )
6185                if not var:
6186                    break
6187                options.append(var.name)
6188
6189        return options
6190
6191    def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]:
6192        if match and not self._match(TokenType.REFERENCES):
6193            return None
6194
6195        expressions = None
6196        this = self._parse_table(schema=True)
6197        options = self._parse_key_constraint_options()
6198        return self.expression(exp.Reference, this=this, expressions=expressions, options=options)
6199
6200    def _parse_foreign_key(self) -> exp.ForeignKey:
6201        expressions = (
6202            self._parse_wrapped_id_vars()
6203            if not self._match(TokenType.REFERENCES, advance=False)
6204            else None
6205        )
6206        reference = self._parse_references()
6207        on_options = {}
6208
6209        while self._match(TokenType.ON):
6210            if not self._match_set((TokenType.DELETE, TokenType.UPDATE)):
6211                self.raise_error("Expected DELETE or UPDATE")
6212
6213            kind = self._prev.text.lower()
6214
6215            if self._match_text_seq("NO", "ACTION"):
6216                action = "NO ACTION"
6217            elif self._match(TokenType.SET):
6218                self._match_set((TokenType.NULL, TokenType.DEFAULT))
6219                action = "SET " + self._prev.text.upper()
6220            else:
6221                self._advance()
6222                action = self._prev.text.upper()
6223
6224            on_options[kind] = action
6225
6226        return self.expression(
6227            exp.ForeignKey,
6228            expressions=expressions,
6229            reference=reference,
6230            options=self._parse_key_constraint_options(),
6231            **on_options,  # type: ignore
6232        )
6233
6234    def _parse_primary_key_part(self) -> t.Optional[exp.Expression]:
6235        return self._parse_ordered() or self._parse_field()
6236
6237    def _parse_period_for_system_time(self) -> t.Optional[exp.PeriodForSystemTimeConstraint]:
6238        if not self._match(TokenType.TIMESTAMP_SNAPSHOT):
6239            self._retreat(self._index - 1)
6240            return None
6241
6242        id_vars = self._parse_wrapped_id_vars()
6243        return self.expression(
6244            exp.PeriodForSystemTimeConstraint,
6245            this=seq_get(id_vars, 0),
6246            expression=seq_get(id_vars, 1),
6247        )
6248
6249    def _parse_primary_key(
6250        self, wrapped_optional: bool = False, in_props: bool = False
6251    ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey:
6252        desc = (
6253            self._match_set((TokenType.ASC, TokenType.DESC))
6254            and self._prev.token_type == TokenType.DESC
6255        )
6256
6257        if not in_props and not self._match(TokenType.L_PAREN, advance=False):
6258            return self.expression(
6259                exp.PrimaryKeyColumnConstraint,
6260                desc=desc,
6261                options=self._parse_key_constraint_options(),
6262            )
6263
6264        expressions = self._parse_wrapped_csv(
6265            self._parse_primary_key_part, optional=wrapped_optional
6266        )
6267        options = self._parse_key_constraint_options()
6268        return self.expression(exp.PrimaryKey, expressions=expressions, options=options)
6269
6270    def _parse_bracket_key_value(self, is_map: bool = False) -> t.Optional[exp.Expression]:
6271        return self._parse_slice(self._parse_alias(self._parse_assignment(), explicit=True))
6272
6273    def _parse_odbc_datetime_literal(self) -> exp.Expression:
6274        """
6275        Parses a datetime column in ODBC format. We parse the column into the corresponding
6276        types, for example `{d'yyyy-mm-dd'}` will be parsed as a `Date` column, exactly the
6277        same as we did for `DATE('yyyy-mm-dd')`.
6278
6279        Reference:
6280        https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/date-time-and-timestamp-literals
6281        """
6282        self._match(TokenType.VAR)
6283        exp_class = self.ODBC_DATETIME_LITERALS[self._prev.text.lower()]
6284        expression = self.expression(exp_class=exp_class, this=self._parse_string())
6285        if not self._match(TokenType.R_BRACE):
6286            self.raise_error("Expected }")
6287        return expression
6288
6289    def _parse_bracket(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]:
6290        if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)):
6291            return this
6292
6293        bracket_kind = self._prev.token_type
6294        if (
6295            bracket_kind == TokenType.L_BRACE
6296            and self._curr
6297            and self._curr.token_type == TokenType.VAR
6298            and self._curr.text.lower() in self.ODBC_DATETIME_LITERALS
6299        ):
6300            return self._parse_odbc_datetime_literal()
6301
6302        expressions = self._parse_csv(
6303            lambda: self._parse_bracket_key_value(is_map=bracket_kind == TokenType.L_BRACE)
6304        )
6305
6306        if bracket_kind == TokenType.L_BRACKET and not self._match(TokenType.R_BRACKET):
6307            self.raise_error("Expected ]")
6308        elif bracket_kind == TokenType.L_BRACE and not self._match(TokenType.R_BRACE):
6309            self.raise_error("Expected }")
6310
6311        # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
6312        if bracket_kind == TokenType.L_BRACE:
6313            this = self.expression(exp.Struct, expressions=self._kv_to_prop_eq(expressions))
6314        elif not this:
6315            this = build_array_constructor(
6316                exp.Array, args=expressions, bracket_kind=bracket_kind, dialect=self.dialect
6317            )
6318        else:
6319            constructor_type = self.ARRAY_CONSTRUCTORS.get(this.name.upper())
6320            if constructor_type:
6321                return build_array_constructor(
6322                    constructor_type,
6323                    args=expressions,
6324                    bracket_kind=bracket_kind,
6325                    dialect=self.dialect,
6326                )
6327
6328            expressions = apply_index_offset(
6329                this, expressions, -self.dialect.INDEX_OFFSET, dialect=self.dialect
6330            )
6331            this = self.expression(
6332                exp.Bracket,
6333                this=this,
6334                expressions=expressions,
6335                comments=this.pop_comments(),
6336            )
6337
6338        self._add_comments(this)
6339        return self._parse_bracket(this)
6340
6341    def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6342        if self._match(TokenType.COLON):
6343            return self.expression(exp.Slice, this=this, expression=self._parse_assignment())
6344        return this
6345
6346    def _parse_case(self) -> t.Optional[exp.Expression]:
6347        ifs = []
6348        default = None
6349
6350        comments = self._prev_comments
6351        expression = self._parse_assignment()
6352
6353        while self._match(TokenType.WHEN):
6354            this = self._parse_assignment()
6355            self._match(TokenType.THEN)
6356            then = self._parse_assignment()
6357            ifs.append(self.expression(exp.If, this=this, true=then))
6358
6359        if self._match(TokenType.ELSE):
6360            default = self._parse_assignment()
6361
6362        if not self._match(TokenType.END):
6363            if isinstance(default, exp.Interval) and default.this.sql().upper() == "END":
6364                default = exp.column("interval")
6365            else:
6366                self.raise_error("Expected END after CASE", self._prev)
6367
6368        return self.expression(
6369            exp.Case, comments=comments, this=expression, ifs=ifs, default=default
6370        )
6371
6372    def _parse_if(self) -> t.Optional[exp.Expression]:
6373        if self._match(TokenType.L_PAREN):
6374            args = self._parse_csv(
6375                lambda: self._parse_alias(self._parse_assignment(), explicit=True)
6376            )
6377            this = self.validate_expression(exp.If.from_arg_list(args), args)
6378            self._match_r_paren()
6379        else:
6380            index = self._index - 1
6381
6382            if self.NO_PAREN_IF_COMMANDS and index == 0:
6383                return self._parse_as_command(self._prev)
6384
6385            condition = self._parse_assignment()
6386
6387            if not condition:
6388                self._retreat(index)
6389                return None
6390
6391            self._match(TokenType.THEN)
6392            true = self._parse_assignment()
6393            false = self._parse_assignment() if self._match(TokenType.ELSE) else None
6394            self._match(TokenType.END)
6395            this = self.expression(exp.If, this=condition, true=true, false=false)
6396
6397        return this
6398
6399    def _parse_next_value_for(self) -> t.Optional[exp.Expression]:
6400        if not self._match_text_seq("VALUE", "FOR"):
6401            self._retreat(self._index - 1)
6402            return None
6403
6404        return self.expression(
6405            exp.NextValueFor,
6406            this=self._parse_column(),
6407            order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order),
6408        )
6409
6410    def _parse_extract(self) -> exp.Extract | exp.Anonymous:
6411        this = self._parse_function() or self._parse_var_or_string(upper=True)
6412
6413        if self._match(TokenType.FROM):
6414            return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6415
6416        if not self._match(TokenType.COMMA):
6417            self.raise_error("Expected FROM or comma after EXTRACT", self._prev)
6418
6419        return self.expression(exp.Extract, this=this, expression=self._parse_bitwise())
6420
6421    def _parse_gap_fill(self) -> exp.GapFill:
6422        self._match(TokenType.TABLE)
6423        this = self._parse_table()
6424
6425        self._match(TokenType.COMMA)
6426        args = [this, *self._parse_csv(self._parse_lambda)]
6427
6428        gap_fill = exp.GapFill.from_arg_list(args)
6429        return self.validate_expression(gap_fill, args)
6430
6431    def _parse_cast(self, strict: bool, safe: t.Optional[bool] = None) -> exp.Expression:
6432        this = self._parse_assignment()
6433
6434        if not self._match(TokenType.ALIAS):
6435            if self._match(TokenType.COMMA):
6436                return self.expression(exp.CastToStrType, this=this, to=self._parse_string())
6437
6438            self.raise_error("Expected AS after CAST")
6439
6440        fmt = None
6441        to = self._parse_types()
6442
6443        default = self._match(TokenType.DEFAULT)
6444        if default:
6445            default = self._parse_bitwise()
6446            self._match_text_seq("ON", "CONVERSION", "ERROR")
6447
6448        if self._match_set((TokenType.FORMAT, TokenType.COMMA)):
6449            fmt_string = self._parse_string()
6450            fmt = self._parse_at_time_zone(fmt_string)
6451
6452            if not to:
6453                to = exp.DataType.build(exp.DataType.Type.UNKNOWN)
6454            if to.this in exp.DataType.TEMPORAL_TYPES:
6455                this = self.expression(
6456                    exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime,
6457                    this=this,
6458                    format=exp.Literal.string(
6459                        format_time(
6460                            fmt_string.this if fmt_string else "",
6461                            self.dialect.FORMAT_MAPPING or self.dialect.TIME_MAPPING,
6462                            self.dialect.FORMAT_TRIE or self.dialect.TIME_TRIE,
6463                        )
6464                    ),
6465                    safe=safe,
6466                )
6467
6468                if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime):
6469                    this.set("zone", fmt.args["zone"])
6470                return this
6471        elif not to:
6472            self.raise_error("Expected TYPE after CAST")
6473        elif isinstance(to, exp.Identifier):
6474            to = exp.DataType.build(to.name, udt=True)
6475        elif to.this == exp.DataType.Type.CHAR:
6476            if self._match(TokenType.CHARACTER_SET):
6477                to = self.expression(exp.CharacterSet, this=self._parse_var_or_string())
6478
6479        return self.expression(
6480            exp.Cast if strict else exp.TryCast,
6481            this=this,
6482            to=to,
6483            format=fmt,
6484            safe=safe,
6485            action=self._parse_var_from_options(self.CAST_ACTIONS, raise_unmatched=False),
6486            default=default,
6487        )
6488
6489    def _parse_string_agg(self) -> exp.GroupConcat:
6490        if self._match(TokenType.DISTINCT):
6491            args: t.List[t.Optional[exp.Expression]] = [
6492                self.expression(exp.Distinct, expressions=[self._parse_assignment()])
6493            ]
6494            if self._match(TokenType.COMMA):
6495                args.extend(self._parse_csv(self._parse_assignment))
6496        else:
6497            args = self._parse_csv(self._parse_assignment)  # type: ignore
6498
6499        if self._match_text_seq("ON", "OVERFLOW"):
6500            # trino: LISTAGG(expression [, separator] [ON OVERFLOW overflow_behavior])
6501            if self._match_text_seq("ERROR"):
6502                on_overflow: t.Optional[exp.Expression] = exp.var("ERROR")
6503            else:
6504                self._match_text_seq("TRUNCATE")
6505                on_overflow = self.expression(
6506                    exp.OverflowTruncateBehavior,
6507                    this=self._parse_string(),
6508                    with_count=(
6509                        self._match_text_seq("WITH", "COUNT")
6510                        or not self._match_text_seq("WITHOUT", "COUNT")
6511                    ),
6512                )
6513        else:
6514            on_overflow = None
6515
6516        index = self._index
6517        if not self._match(TokenType.R_PAREN) and args:
6518            # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]])
6519            # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n])
6520            # The order is parsed through `this` as a canonicalization for WITHIN GROUPs
6521            args[0] = self._parse_limit(this=self._parse_order(this=args[0]))
6522            return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1))
6523
6524        # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]).
6525        # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that
6526        # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them.
6527        if not self._match_text_seq("WITHIN", "GROUP"):
6528            self._retreat(index)
6529            return self.validate_expression(exp.GroupConcat.from_arg_list(args), args)
6530
6531        # The corresponding match_r_paren will be called in parse_function (caller)
6532        self._match_l_paren()
6533
6534        return self.expression(
6535            exp.GroupConcat,
6536            this=self._parse_order(this=seq_get(args, 0)),
6537            separator=seq_get(args, 1),
6538            on_overflow=on_overflow,
6539        )
6540
6541    def _parse_convert(
6542        self, strict: bool, safe: t.Optional[bool] = None
6543    ) -> t.Optional[exp.Expression]:
6544        this = self._parse_bitwise()
6545
6546        if self._match(TokenType.USING):
6547            to: t.Optional[exp.Expression] = self.expression(
6548                exp.CharacterSet, this=self._parse_var()
6549            )
6550        elif self._match(TokenType.COMMA):
6551            to = self._parse_types()
6552        else:
6553            to = None
6554
6555        return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, safe=safe)
6556
6557    def _parse_xml_table(self) -> exp.XMLTable:
6558        namespaces = None
6559        passing = None
6560        columns = None
6561
6562        if self._match_text_seq("XMLNAMESPACES", "("):
6563            namespaces = self._parse_xml_namespace()
6564            self._match_text_seq(")", ",")
6565
6566        this = self._parse_string()
6567
6568        if self._match_text_seq("PASSING"):
6569            # The BY VALUE keywords are optional and are provided for semantic clarity
6570            self._match_text_seq("BY", "VALUE")
6571            passing = self._parse_csv(self._parse_column)
6572
6573        by_ref = self._match_text_seq("RETURNING", "SEQUENCE", "BY", "REF")
6574
6575        if self._match_text_seq("COLUMNS"):
6576            columns = self._parse_csv(self._parse_field_def)
6577
6578        return self.expression(
6579            exp.XMLTable,
6580            this=this,
6581            namespaces=namespaces,
6582            passing=passing,
6583            columns=columns,
6584            by_ref=by_ref,
6585        )
6586
6587    def _parse_xml_namespace(self) -> t.List[exp.XMLNamespace]:
6588        namespaces = []
6589
6590        while True:
6591            if self._match(TokenType.DEFAULT):
6592                uri = self._parse_string()
6593            else:
6594                uri = self._parse_alias(self._parse_string())
6595            namespaces.append(self.expression(exp.XMLNamespace, this=uri))
6596            if not self._match(TokenType.COMMA):
6597                break
6598
6599        return namespaces
6600
6601    def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]:
6602        """
6603        There are generally two variants of the DECODE function:
6604
6605        - DECODE(bin, charset)
6606        - DECODE(expression, search, result [, search, result] ... [, default])
6607
6608        The second variant will always be parsed into a CASE expression. Note that NULL
6609        needs special treatment, since we need to explicitly check for it with `IS NULL`,
6610        instead of relying on pattern matching.
6611        """
6612        args = self._parse_csv(self._parse_assignment)
6613
6614        if len(args) < 3:
6615            return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1))
6616
6617        expression, *expressions = args
6618        if not expression:
6619            return None
6620
6621        ifs = []
6622        for search, result in zip(expressions[::2], expressions[1::2]):
6623            if not search or not result:
6624                return None
6625
6626            if isinstance(search, exp.Literal):
6627                ifs.append(
6628                    exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result)
6629                )
6630            elif isinstance(search, exp.Null):
6631                ifs.append(
6632                    exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result)
6633                )
6634            else:
6635                cond = exp.or_(
6636                    exp.EQ(this=expression.copy(), expression=search),
6637                    exp.and_(
6638                        exp.Is(this=expression.copy(), expression=exp.Null()),
6639                        exp.Is(this=search.copy(), expression=exp.Null()),
6640                        copy=False,
6641                    ),
6642                    copy=False,
6643                )
6644                ifs.append(exp.If(this=cond, true=result))
6645
6646        return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None)
6647
6648    def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]:
6649        self._match_text_seq("KEY")
6650        key = self._parse_column()
6651        self._match_set(self.JSON_KEY_VALUE_SEPARATOR_TOKENS)
6652        self._match_text_seq("VALUE")
6653        value = self._parse_bitwise()
6654
6655        if not key and not value:
6656            return None
6657        return self.expression(exp.JSONKeyValue, this=key, expression=value)
6658
6659    def _parse_format_json(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6660        if not this or not self._match_text_seq("FORMAT", "JSON"):
6661            return this
6662
6663        return self.expression(exp.FormatJson, this=this)
6664
6665    def _parse_on_condition(self) -> t.Optional[exp.OnCondition]:
6666        # MySQL uses "X ON EMPTY Y ON ERROR" (e.g. JSON_VALUE) while Oracle uses the opposite (e.g. JSON_EXISTS)
6667        if self.dialect.ON_CONDITION_EMPTY_BEFORE_ERROR:
6668            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6669            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6670        else:
6671            error = self._parse_on_handling("ERROR", *self.ON_CONDITION_TOKENS)
6672            empty = self._parse_on_handling("EMPTY", *self.ON_CONDITION_TOKENS)
6673
6674        null = self._parse_on_handling("NULL", *self.ON_CONDITION_TOKENS)
6675
6676        if not empty and not error and not null:
6677            return None
6678
6679        return self.expression(
6680            exp.OnCondition,
6681            empty=empty,
6682            error=error,
6683            null=null,
6684        )
6685
6686    def _parse_on_handling(
6687        self, on: str, *values: str
6688    ) -> t.Optional[str] | t.Optional[exp.Expression]:
6689        # Parses the "X ON Y" or "DEFAULT <expr> ON Y syntax, e.g. NULL ON NULL (Oracle, T-SQL, MySQL)
6690        for value in values:
6691            if self._match_text_seq(value, "ON", on):
6692                return f"{value} ON {on}"
6693
6694        index = self._index
6695        if self._match(TokenType.DEFAULT):
6696            default_value = self._parse_bitwise()
6697            if self._match_text_seq("ON", on):
6698                return default_value
6699
6700            self._retreat(index)
6701
6702        return None
6703
6704    @t.overload
6705    def _parse_json_object(self, agg: Lit[False]) -> exp.JSONObject: ...
6706
6707    @t.overload
6708    def _parse_json_object(self, agg: Lit[True]) -> exp.JSONObjectAgg: ...
6709
6710    def _parse_json_object(self, agg=False):
6711        star = self._parse_star()
6712        expressions = (
6713            [star]
6714            if star
6715            else self._parse_csv(lambda: self._parse_format_json(self._parse_json_key_value()))
6716        )
6717        null_handling = self._parse_on_handling("NULL", "NULL", "ABSENT")
6718
6719        unique_keys = None
6720        if self._match_text_seq("WITH", "UNIQUE"):
6721            unique_keys = True
6722        elif self._match_text_seq("WITHOUT", "UNIQUE"):
6723            unique_keys = False
6724
6725        self._match_text_seq("KEYS")
6726
6727        return_type = self._match_text_seq("RETURNING") and self._parse_format_json(
6728            self._parse_type()
6729        )
6730        encoding = self._match_text_seq("ENCODING") and self._parse_var()
6731
6732        return self.expression(
6733            exp.JSONObjectAgg if agg else exp.JSONObject,
6734            expressions=expressions,
6735            null_handling=null_handling,
6736            unique_keys=unique_keys,
6737            return_type=return_type,
6738            encoding=encoding,
6739        )
6740
6741    # Note: this is currently incomplete; it only implements the "JSON_value_column" part
6742    def _parse_json_column_def(self) -> exp.JSONColumnDef:
6743        if not self._match_text_seq("NESTED"):
6744            this = self._parse_id_var()
6745            kind = self._parse_types(allow_identifiers=False)
6746            nested = None
6747        else:
6748            this = None
6749            kind = None
6750            nested = True
6751
6752        path = self._match_text_seq("PATH") and self._parse_string()
6753        nested_schema = nested and self._parse_json_schema()
6754
6755        return self.expression(
6756            exp.JSONColumnDef,
6757            this=this,
6758            kind=kind,
6759            path=path,
6760            nested_schema=nested_schema,
6761        )
6762
6763    def _parse_json_schema(self) -> exp.JSONSchema:
6764        self._match_text_seq("COLUMNS")
6765        return self.expression(
6766            exp.JSONSchema,
6767            expressions=self._parse_wrapped_csv(self._parse_json_column_def, optional=True),
6768        )
6769
6770    def _parse_json_table(self) -> exp.JSONTable:
6771        this = self._parse_format_json(self._parse_bitwise())
6772        path = self._match(TokenType.COMMA) and self._parse_string()
6773        error_handling = self._parse_on_handling("ERROR", "ERROR", "NULL")
6774        empty_handling = self._parse_on_handling("EMPTY", "ERROR", "NULL")
6775        schema = self._parse_json_schema()
6776
6777        return exp.JSONTable(
6778            this=this,
6779            schema=schema,
6780            path=path,
6781            error_handling=error_handling,
6782            empty_handling=empty_handling,
6783        )
6784
6785    def _parse_match_against(self) -> exp.MatchAgainst:
6786        expressions = self._parse_csv(self._parse_column)
6787
6788        self._match_text_seq(")", "AGAINST", "(")
6789
6790        this = self._parse_string()
6791
6792        if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"):
6793            modifier = "IN NATURAL LANGUAGE MODE"
6794            if self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6795                modifier = f"{modifier} WITH QUERY EXPANSION"
6796        elif self._match_text_seq("IN", "BOOLEAN", "MODE"):
6797            modifier = "IN BOOLEAN MODE"
6798        elif self._match_text_seq("WITH", "QUERY", "EXPANSION"):
6799            modifier = "WITH QUERY EXPANSION"
6800        else:
6801            modifier = None
6802
6803        return self.expression(
6804            exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier
6805        )
6806
6807    # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16
6808    def _parse_open_json(self) -> exp.OpenJSON:
6809        this = self._parse_bitwise()
6810        path = self._match(TokenType.COMMA) and self._parse_string()
6811
6812        def _parse_open_json_column_def() -> exp.OpenJSONColumnDef:
6813            this = self._parse_field(any_token=True)
6814            kind = self._parse_types()
6815            path = self._parse_string()
6816            as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON)
6817
6818            return self.expression(
6819                exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json
6820            )
6821
6822        expressions = None
6823        if self._match_pair(TokenType.R_PAREN, TokenType.WITH):
6824            self._match_l_paren()
6825            expressions = self._parse_csv(_parse_open_json_column_def)
6826
6827        return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions)
6828
6829    def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition:
6830        args = self._parse_csv(self._parse_bitwise)
6831
6832        if self._match(TokenType.IN):
6833            return self.expression(
6834                exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0)
6835            )
6836
6837        if haystack_first:
6838            haystack = seq_get(args, 0)
6839            needle = seq_get(args, 1)
6840        else:
6841            haystack = seq_get(args, 1)
6842            needle = seq_get(args, 0)
6843
6844        return self.expression(
6845            exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2)
6846        )
6847
6848    def _parse_predict(self) -> exp.Predict:
6849        self._match_text_seq("MODEL")
6850        this = self._parse_table()
6851
6852        self._match(TokenType.COMMA)
6853        self._match_text_seq("TABLE")
6854
6855        return self.expression(
6856            exp.Predict,
6857            this=this,
6858            expression=self._parse_table(),
6859            params_struct=self._match(TokenType.COMMA) and self._parse_bitwise(),
6860        )
6861
6862    def _parse_join_hint(self, func_name: str) -> exp.JoinHint:
6863        args = self._parse_csv(self._parse_table)
6864        return exp.JoinHint(this=func_name.upper(), expressions=args)
6865
6866    def _parse_substring(self) -> exp.Substring:
6867        # Postgres supports the form: substring(string [from int] [for int])
6868        # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6
6869
6870        args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise))
6871
6872        if self._match(TokenType.FROM):
6873            args.append(self._parse_bitwise())
6874        if self._match(TokenType.FOR):
6875            if len(args) == 1:
6876                args.append(exp.Literal.number(1))
6877            args.append(self._parse_bitwise())
6878
6879        return self.validate_expression(exp.Substring.from_arg_list(args), args)
6880
6881    def _parse_trim(self) -> exp.Trim:
6882        # https://www.w3resource.com/sql/character-functions/trim.php
6883        # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html
6884
6885        position = None
6886        collation = None
6887        expression = None
6888
6889        if self._match_texts(self.TRIM_TYPES):
6890            position = self._prev.text.upper()
6891
6892        this = self._parse_bitwise()
6893        if self._match_set((TokenType.FROM, TokenType.COMMA)):
6894            invert_order = self._prev.token_type == TokenType.FROM or self.TRIM_PATTERN_FIRST
6895            expression = self._parse_bitwise()
6896
6897            if invert_order:
6898                this, expression = expression, this
6899
6900        if self._match(TokenType.COLLATE):
6901            collation = self._parse_bitwise()
6902
6903        return self.expression(
6904            exp.Trim, this=this, position=position, expression=expression, collation=collation
6905        )
6906
6907    def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]:
6908        return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window)
6909
6910    def _parse_named_window(self) -> t.Optional[exp.Expression]:
6911        return self._parse_window(self._parse_id_var(), alias=True)
6912
6913    def _parse_respect_or_ignore_nulls(
6914        self, this: t.Optional[exp.Expression]
6915    ) -> t.Optional[exp.Expression]:
6916        if self._match_text_seq("IGNORE", "NULLS"):
6917            return self.expression(exp.IgnoreNulls, this=this)
6918        if self._match_text_seq("RESPECT", "NULLS"):
6919            return self.expression(exp.RespectNulls, this=this)
6920        return this
6921
6922    def _parse_having_max(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]:
6923        if self._match(TokenType.HAVING):
6924            self._match_texts(("MAX", "MIN"))
6925            max = self._prev.text.upper() != "MIN"
6926            return self.expression(
6927                exp.HavingMax, this=this, expression=self._parse_column(), max=max
6928            )
6929
6930        return this
6931
6932    def _parse_window(
6933        self, this: t.Optional[exp.Expression], alias: bool = False
6934    ) -> t.Optional[exp.Expression]:
6935        func = this
6936        comments = func.comments if isinstance(func, exp.Expression) else None
6937
6938        # T-SQL allows the OVER (...) syntax after WITHIN GROUP.
6939        # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16
6940        if self._match_text_seq("WITHIN", "GROUP"):
6941            order = self._parse_wrapped(self._parse_order)
6942            this = self.expression(exp.WithinGroup, this=this, expression=order)
6943
6944        if self._match_pair(TokenType.FILTER, TokenType.L_PAREN):
6945            self._match(TokenType.WHERE)
6946            this = self.expression(
6947                exp.Filter, this=this, expression=self._parse_where(skip_where_token=True)
6948            )
6949            self._match_r_paren()
6950
6951        # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER
6952        # Some dialects choose to implement and some do not.
6953        # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
6954
6955        # There is some code above in _parse_lambda that handles
6956        #   SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ...
6957
6958        # The below changes handle
6959        #   SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ...
6960
6961        # Oracle allows both formats
6962        #   (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html)
6963        #   and Snowflake chose to do the same for familiarity
6964        #   https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes
6965        if isinstance(this, exp.AggFunc):
6966            ignore_respect = this.find(exp.IgnoreNulls, exp.RespectNulls)
6967
6968            if ignore_respect and ignore_respect is not this:
6969                ignore_respect.replace(ignore_respect.this)
6970                this = self.expression(ignore_respect.__class__, this=this)
6971
6972        this = self._parse_respect_or_ignore_nulls(this)
6973
6974        # bigquery select from window x AS (partition by ...)
6975        if alias:
6976            over = None
6977            self._match(TokenType.ALIAS)
6978        elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS):
6979            return this
6980        else:
6981            over = self._prev.text.upper()
6982
6983        if comments and isinstance(func, exp.Expression):
6984            func.pop_comments()
6985
6986        if not self._match(TokenType.L_PAREN):
6987            return self.expression(
6988                exp.Window,
6989                comments=comments,
6990                this=this,
6991                alias=self._parse_id_var(False),
6992                over=over,
6993            )
6994
6995        window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS)
6996
6997        first = self._match(TokenType.FIRST)
6998        if self._match_text_seq("LAST"):
6999            first = False
7000
7001        partition, order = self._parse_partition_and_order()
7002        kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text
7003
7004        if kind:
7005            self._match(TokenType.BETWEEN)
7006            start = self._parse_window_spec()
7007            self._match(TokenType.AND)
7008            end = self._parse_window_spec()
7009            exclude = (
7010                self._parse_var_from_options(self.WINDOW_EXCLUDE_OPTIONS)
7011                if self._match_text_seq("EXCLUDE")
7012                else None
7013            )
7014
7015            spec = self.expression(
7016                exp.WindowSpec,
7017                kind=kind,
7018                start=start["value"],
7019                start_side=start["side"],
7020                end=end["value"],
7021                end_side=end["side"],
7022                exclude=exclude,
7023            )
7024        else:
7025            spec = None
7026
7027        self._match_r_paren()
7028
7029        window = self.expression(
7030            exp.Window,
7031            comments=comments,
7032            this=this,
7033            partition_by=partition,
7034            order=order,
7035            spec=spec,
7036            alias=window_alias,
7037            over=over,
7038            first=first,
7039        )
7040
7041        # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...)
7042        if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False):
7043            return self._parse_window(window, alias=alias)
7044
7045        return window
7046
7047    def _parse_partition_and_order(
7048        self,
7049    ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]:
7050        return self._parse_partition_by(), self._parse_order()
7051
7052    def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]:
7053        self._match(TokenType.BETWEEN)
7054
7055        return {
7056            "value": (
7057                (self._match_text_seq("UNBOUNDED") and "UNBOUNDED")
7058                or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW")
7059                or self._parse_bitwise()
7060            ),
7061            "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text,
7062        }
7063
7064    def _parse_alias(
7065        self, this: t.Optional[exp.Expression], explicit: bool = False
7066    ) -> t.Optional[exp.Expression]:
7067        # In some dialects, LIMIT and OFFSET can act as both identifiers and keywords (clauses)
7068        # so this section tries to parse the clause version and if it fails, it treats the token
7069        # as an identifier (alias)
7070        if self._can_parse_limit_or_offset():
7071            return this
7072
7073        any_token = self._match(TokenType.ALIAS)
7074        comments = self._prev_comments or []
7075
7076        if explicit and not any_token:
7077            return this
7078
7079        if self._match(TokenType.L_PAREN):
7080            aliases = self.expression(
7081                exp.Aliases,
7082                comments=comments,
7083                this=this,
7084                expressions=self._parse_csv(lambda: self._parse_id_var(any_token)),
7085            )
7086            self._match_r_paren(aliases)
7087            return aliases
7088
7089        alias = self._parse_id_var(any_token, tokens=self.ALIAS_TOKENS) or (
7090            self.STRING_ALIASES and self._parse_string_as_identifier()
7091        )
7092
7093        if alias:
7094            comments.extend(alias.pop_comments())
7095            this = self.expression(exp.Alias, comments=comments, this=this, alias=alias)
7096            column = this.this
7097
7098            # Moves the comment next to the alias in `expr /* comment */ AS alias`
7099            if not this.comments and column and column.comments:
7100                this.comments = column.pop_comments()
7101
7102        return this
7103
7104    def _parse_id_var(
7105        self,
7106        any_token: bool = True,
7107        tokens: t.Optional[t.Collection[TokenType]] = None,
7108    ) -> t.Optional[exp.Expression]:
7109        expression = self._parse_identifier()
7110        if not expression and (
7111            (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS)
7112        ):
7113            quoted = self._prev.token_type == TokenType.STRING
7114            expression = self._identifier_expression(quoted=quoted)
7115
7116        return expression
7117
7118    def _parse_string(self) -> t.Optional[exp.Expression]:
7119        if self._match_set(self.STRING_PARSERS):
7120            return self.STRING_PARSERS[self._prev.token_type](self, self._prev)
7121        return self._parse_placeholder()
7122
7123    def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]:
7124        output = exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True)
7125        if output:
7126            output.update_positions(self._prev)
7127        return output
7128
7129    def _parse_number(self) -> t.Optional[exp.Expression]:
7130        if self._match_set(self.NUMERIC_PARSERS):
7131            return self.NUMERIC_PARSERS[self._prev.token_type](self, self._prev)
7132        return self._parse_placeholder()
7133
7134    def _parse_identifier(self) -> t.Optional[exp.Expression]:
7135        if self._match(TokenType.IDENTIFIER):
7136            return self._identifier_expression(quoted=True)
7137        return self._parse_placeholder()
7138
7139    def _parse_var(
7140        self,
7141        any_token: bool = False,
7142        tokens: t.Optional[t.Collection[TokenType]] = None,
7143        upper: bool = False,
7144    ) -> t.Optional[exp.Expression]:
7145        if (
7146            (any_token and self._advance_any())
7147            or self._match(TokenType.VAR)
7148            or (self._match_set(tokens) if tokens else False)
7149        ):
7150            return self.expression(
7151                exp.Var, this=self._prev.text.upper() if upper else self._prev.text
7152            )
7153        return self._parse_placeholder()
7154
7155    def _advance_any(self, ignore_reserved: bool = False) -> t.Optional[Token]:
7156        if self._curr and (ignore_reserved or self._curr.token_type not in self.RESERVED_TOKENS):
7157            self._advance()
7158            return self._prev
7159        return None
7160
7161    def _parse_var_or_string(self, upper: bool = False) -> t.Optional[exp.Expression]:
7162        return self._parse_string() or self._parse_var(any_token=True, upper=upper)
7163
7164    def _parse_primary_or_var(self) -> t.Optional[exp.Expression]:
7165        return self._parse_primary() or self._parse_var(any_token=True)
7166
7167    def _parse_null(self) -> t.Optional[exp.Expression]:
7168        if self._match_set(self.NULL_TOKENS):
7169            return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev)
7170        return self._parse_placeholder()
7171
7172    def _parse_boolean(self) -> t.Optional[exp.Expression]:
7173        if self._match(TokenType.TRUE):
7174            return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev)
7175        if self._match(TokenType.FALSE):
7176            return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev)
7177        return self._parse_placeholder()
7178
7179    def _parse_star(self) -> t.Optional[exp.Expression]:
7180        if self._match(TokenType.STAR):
7181            return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev)
7182        return self._parse_placeholder()
7183
7184    def _parse_parameter(self) -> exp.Parameter:
7185        this = self._parse_identifier() or self._parse_primary_or_var()
7186        return self.expression(exp.Parameter, this=this)
7187
7188    def _parse_placeholder(self) -> t.Optional[exp.Expression]:
7189        if self._match_set(self.PLACEHOLDER_PARSERS):
7190            placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self)
7191            if placeholder:
7192                return placeholder
7193            self._advance(-1)
7194        return None
7195
7196    def _parse_star_op(self, *keywords: str) -> t.Optional[t.List[exp.Expression]]:
7197        if not self._match_texts(keywords):
7198            return None
7199        if self._match(TokenType.L_PAREN, advance=False):
7200            return self._parse_wrapped_csv(self._parse_expression)
7201
7202        expression = self._parse_expression()
7203        return [expression] if expression else None
7204
7205    def _parse_csv(
7206        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA
7207    ) -> t.List[exp.Expression]:
7208        parse_result = parse_method()
7209        items = [parse_result] if parse_result is not None else []
7210
7211        while self._match(sep):
7212            self._add_comments(parse_result)
7213            parse_result = parse_method()
7214            if parse_result is not None:
7215                items.append(parse_result)
7216
7217        return items
7218
7219    def _parse_tokens(
7220        self, parse_method: t.Callable, expressions: t.Dict
7221    ) -> t.Optional[exp.Expression]:
7222        this = parse_method()
7223
7224        while self._match_set(expressions):
7225            this = self.expression(
7226                expressions[self._prev.token_type],
7227                this=this,
7228                comments=self._prev_comments,
7229                expression=parse_method(),
7230            )
7231
7232        return this
7233
7234    def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]:
7235        return self._parse_wrapped_csv(self._parse_id_var, optional=optional)
7236
7237    def _parse_wrapped_csv(
7238        self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False
7239    ) -> t.List[exp.Expression]:
7240        return self._parse_wrapped(
7241            lambda: self._parse_csv(parse_method, sep=sep), optional=optional
7242        )
7243
7244    def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any:
7245        wrapped = self._match(TokenType.L_PAREN)
7246        if not wrapped and not optional:
7247            self.raise_error("Expecting (")
7248        parse_result = parse_method()
7249        if wrapped:
7250            self._match_r_paren()
7251        return parse_result
7252
7253    def _parse_expressions(self) -> t.List[exp.Expression]:
7254        return self._parse_csv(self._parse_expression)
7255
7256    def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]:
7257        return self._parse_select() or self._parse_set_operations(
7258            self._parse_alias(self._parse_assignment(), explicit=True)
7259            if alias
7260            else self._parse_assignment()
7261        )
7262
7263    def _parse_ddl_select(self) -> t.Optional[exp.Expression]:
7264        return self._parse_query_modifiers(
7265            self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False))
7266        )
7267
7268    def _parse_transaction(self) -> exp.Transaction | exp.Command:
7269        this = None
7270        if self._match_texts(self.TRANSACTION_KIND):
7271            this = self._prev.text
7272
7273        self._match_texts(("TRANSACTION", "WORK"))
7274
7275        modes = []
7276        while True:
7277            mode = []
7278            while self._match(TokenType.VAR):
7279                mode.append(self._prev.text)
7280
7281            if mode:
7282                modes.append(" ".join(mode))
7283            if not self._match(TokenType.COMMA):
7284                break
7285
7286        return self.expression(exp.Transaction, this=this, modes=modes)
7287
7288    def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback:
7289        chain = None
7290        savepoint = None
7291        is_rollback = self._prev.token_type == TokenType.ROLLBACK
7292
7293        self._match_texts(("TRANSACTION", "WORK"))
7294
7295        if self._match_text_seq("TO"):
7296            self._match_text_seq("SAVEPOINT")
7297            savepoint = self._parse_id_var()
7298
7299        if self._match(TokenType.AND):
7300            chain = not self._match_text_seq("NO")
7301            self._match_text_seq("CHAIN")
7302
7303        if is_rollback:
7304            return self.expression(exp.Rollback, savepoint=savepoint)
7305
7306        return self.expression(exp.Commit, chain=chain)
7307
7308    def _parse_refresh(self) -> exp.Refresh:
7309        self._match(TokenType.TABLE)
7310        return self.expression(exp.Refresh, this=self._parse_string() or self._parse_table())
7311
7312    def _parse_add_column(self) -> t.Optional[exp.ColumnDef]:
7313        if not self._prev.text.upper() == "ADD":
7314            return None
7315
7316        start = self._index
7317        self._match(TokenType.COLUMN)
7318
7319        exists_column = self._parse_exists(not_=True)
7320        expression = self._parse_field_def()
7321
7322        if not isinstance(expression, exp.ColumnDef):
7323            self._retreat(start)
7324            return None
7325
7326        expression.set("exists", exists_column)
7327
7328        # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns
7329        if self._match_texts(("FIRST", "AFTER")):
7330            position = self._prev.text
7331            column_position = self.expression(
7332                exp.ColumnPosition, this=self._parse_column(), position=position
7333            )
7334            expression.set("position", column_position)
7335
7336        return expression
7337
7338    def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]:
7339        drop = self._match(TokenType.DROP) and self._parse_drop()
7340        if drop and not isinstance(drop, exp.Command):
7341            drop.set("kind", drop.args.get("kind", "COLUMN"))
7342        return drop
7343
7344    # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html
7345    def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition:
7346        return self.expression(
7347            exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists
7348        )
7349
7350    def _parse_alter_table_add(self) -> t.List[exp.Expression]:
7351        def _parse_add_alteration() -> t.Optional[exp.Expression]:
7352            self._match_text_seq("ADD")
7353            if self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False):
7354                return self.expression(
7355                    exp.AddConstraint, expressions=self._parse_csv(self._parse_constraint)
7356                )
7357
7358            column_def = self._parse_add_column()
7359            if isinstance(column_def, exp.ColumnDef):
7360                return column_def
7361
7362            exists = self._parse_exists(not_=True)
7363            if self._match_pair(TokenType.PARTITION, TokenType.L_PAREN, advance=False):
7364                return self.expression(
7365                    exp.AddPartition, exists=exists, this=self._parse_field(any_token=True)
7366                )
7367
7368            return None
7369
7370        if not self._match_set(self.ADD_CONSTRAINT_TOKENS, advance=False) and (
7371            not self.dialect.ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
7372            or self._match_text_seq("COLUMNS")
7373        ):
7374            schema = self._parse_schema()
7375
7376            return ensure_list(schema) if schema else self._parse_csv(self._parse_field_def)
7377
7378        return self._parse_csv(_parse_add_alteration)
7379
7380    def _parse_alter_table_alter(self) -> t.Optional[exp.Expression]:
7381        if self._match_texts(self.ALTER_ALTER_PARSERS):
7382            return self.ALTER_ALTER_PARSERS[self._prev.text.upper()](self)
7383
7384        # Many dialects support the ALTER [COLUMN] syntax, so if there is no
7385        # keyword after ALTER we default to parsing this statement
7386        self._match(TokenType.COLUMN)
7387        column = self._parse_field(any_token=True)
7388
7389        if self._match_pair(TokenType.DROP, TokenType.DEFAULT):
7390            return self.expression(exp.AlterColumn, this=column, drop=True)
7391        if self._match_pair(TokenType.SET, TokenType.DEFAULT):
7392            return self.expression(exp.AlterColumn, this=column, default=self._parse_assignment())
7393        if self._match(TokenType.COMMENT):
7394            return self.expression(exp.AlterColumn, this=column, comment=self._parse_string())
7395        if self._match_text_seq("DROP", "NOT", "NULL"):
7396            return self.expression(
7397                exp.AlterColumn,
7398                this=column,
7399                drop=True,
7400                allow_null=True,
7401            )
7402        if self._match_text_seq("SET", "NOT", "NULL"):
7403            return self.expression(
7404                exp.AlterColumn,
7405                this=column,
7406                allow_null=False,
7407            )
7408
7409        if self._match_text_seq("SET", "VISIBLE"):
7410            return self.expression(exp.AlterColumn, this=column, visible="VISIBLE")
7411        if self._match_text_seq("SET", "INVISIBLE"):
7412            return self.expression(exp.AlterColumn, this=column, visible="INVISIBLE")
7413
7414        self._match_text_seq("SET", "DATA")
7415        self._match_text_seq("TYPE")
7416        return self.expression(
7417            exp.AlterColumn,
7418            this=column,
7419            dtype=self._parse_types(),
7420            collate=self._match(TokenType.COLLATE) and self._parse_term(),
7421            using=self._match(TokenType.USING) and self._parse_assignment(),
7422        )
7423
7424    def _parse_alter_diststyle(self) -> exp.AlterDistStyle:
7425        if self._match_texts(("ALL", "EVEN", "AUTO")):
7426            return self.expression(exp.AlterDistStyle, this=exp.var(self._prev.text.upper()))
7427
7428        self._match_text_seq("KEY", "DISTKEY")
7429        return self.expression(exp.AlterDistStyle, this=self._parse_column())
7430
7431    def _parse_alter_sortkey(self, compound: t.Optional[bool] = None) -> exp.AlterSortKey:
7432        if compound:
7433            self._match_text_seq("SORTKEY")
7434
7435        if self._match(TokenType.L_PAREN, advance=False):
7436            return self.expression(
7437                exp.AlterSortKey, expressions=self._parse_wrapped_id_vars(), compound=compound
7438            )
7439
7440        self._match_texts(("AUTO", "NONE"))
7441        return self.expression(
7442            exp.AlterSortKey, this=exp.var(self._prev.text.upper()), compound=compound
7443        )
7444
7445    def _parse_alter_table_drop(self) -> t.List[exp.Expression]:
7446        index = self._index - 1
7447
7448        partition_exists = self._parse_exists()
7449        if self._match(TokenType.PARTITION, advance=False):
7450            return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists))
7451
7452        self._retreat(index)
7453        return self._parse_csv(self._parse_drop_column)
7454
7455    def _parse_alter_table_rename(self) -> t.Optional[exp.AlterRename | exp.RenameColumn]:
7456        if self._match(TokenType.COLUMN) or not self.ALTER_RENAME_REQUIRES_COLUMN:
7457            exists = self._parse_exists()
7458            old_column = self._parse_column()
7459            to = self._match_text_seq("TO")
7460            new_column = self._parse_column()
7461
7462            if old_column is None or to is None or new_column is None:
7463                return None
7464
7465            return self.expression(exp.RenameColumn, this=old_column, to=new_column, exists=exists)
7466
7467        self._match_text_seq("TO")
7468        return self.expression(exp.AlterRename, this=self._parse_table(schema=True))
7469
7470    def _parse_alter_table_set(self) -> exp.AlterSet:
7471        alter_set = self.expression(exp.AlterSet)
7472
7473        if self._match(TokenType.L_PAREN, advance=False) or self._match_text_seq(
7474            "TABLE", "PROPERTIES"
7475        ):
7476            alter_set.set("expressions", self._parse_wrapped_csv(self._parse_assignment))
7477        elif self._match_text_seq("FILESTREAM_ON", advance=False):
7478            alter_set.set("expressions", [self._parse_assignment()])
7479        elif self._match_texts(("LOGGED", "UNLOGGED")):
7480            alter_set.set("option", exp.var(self._prev.text.upper()))
7481        elif self._match_text_seq("WITHOUT") and self._match_texts(("CLUSTER", "OIDS")):
7482            alter_set.set("option", exp.var(f"WITHOUT {self._prev.text.upper()}"))
7483        elif self._match_text_seq("LOCATION"):
7484            alter_set.set("location", self._parse_field())
7485        elif self._match_text_seq("ACCESS", "METHOD"):
7486            alter_set.set("access_method", self._parse_field())
7487        elif self._match_text_seq("TABLESPACE"):
7488            alter_set.set("tablespace", self._parse_field())
7489        elif self._match_text_seq("FILE", "FORMAT") or self._match_text_seq("FILEFORMAT"):
7490            alter_set.set("file_format", [self._parse_field()])
7491        elif self._match_text_seq("STAGE_FILE_FORMAT"):
7492            alter_set.set("file_format", self._parse_wrapped_options())
7493        elif self._match_text_seq("STAGE_COPY_OPTIONS"):
7494            alter_set.set("copy_options", self._parse_wrapped_options())
7495        elif self._match_text_seq("TAG") or self._match_text_seq("TAGS"):
7496            alter_set.set("tag", self._parse_csv(self._parse_assignment))
7497        else:
7498            if self._match_text_seq("SERDE"):
7499                alter_set.set("serde", self._parse_field())
7500
7501            properties = self._parse_wrapped(self._parse_properties, optional=True)
7502            alter_set.set("expressions", [properties])
7503
7504        return alter_set
7505
7506    def _parse_alter(self) -> exp.Alter | exp.Command:
7507        start = self._prev
7508
7509        alter_token = self._match_set(self.ALTERABLES) and self._prev
7510        if not alter_token:
7511            return self._parse_as_command(start)
7512
7513        exists = self._parse_exists()
7514        only = self._match_text_seq("ONLY")
7515        this = self._parse_table(schema=True)
7516        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
7517
7518        if self._next:
7519            self._advance()
7520
7521        parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None
7522        if parser:
7523            actions = ensure_list(parser(self))
7524            not_valid = self._match_text_seq("NOT", "VALID")
7525            options = self._parse_csv(self._parse_property)
7526
7527            if not self._curr and actions:
7528                return self.expression(
7529                    exp.Alter,
7530                    this=this,
7531                    kind=alter_token.text.upper(),
7532                    exists=exists,
7533                    actions=actions,
7534                    only=only,
7535                    options=options,
7536                    cluster=cluster,
7537                    not_valid=not_valid,
7538                )
7539
7540        return self._parse_as_command(start)
7541
7542    def _parse_analyze(self) -> exp.Analyze | exp.Command:
7543        start = self._prev
7544        # https://duckdb.org/docs/sql/statements/analyze
7545        if not self._curr:
7546            return self.expression(exp.Analyze)
7547
7548        options = []
7549        while self._match_texts(self.ANALYZE_STYLES):
7550            if self._prev.text.upper() == "BUFFER_USAGE_LIMIT":
7551                options.append(f"BUFFER_USAGE_LIMIT {self._parse_number()}")
7552            else:
7553                options.append(self._prev.text.upper())
7554
7555        this: t.Optional[exp.Expression] = None
7556        inner_expression: t.Optional[exp.Expression] = None
7557
7558        kind = self._curr and self._curr.text.upper()
7559
7560        if self._match(TokenType.TABLE) or self._match(TokenType.INDEX):
7561            this = self._parse_table_parts()
7562        elif self._match_text_seq("TABLES"):
7563            if self._match_set((TokenType.FROM, TokenType.IN)):
7564                kind = f"{kind} {self._prev.text.upper()}"
7565                this = self._parse_table(schema=True, is_db_reference=True)
7566        elif self._match_text_seq("DATABASE"):
7567            this = self._parse_table(schema=True, is_db_reference=True)
7568        elif self._match_text_seq("CLUSTER"):
7569            this = self._parse_table()
7570        # Try matching inner expr keywords before fallback to parse table.
7571        elif self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7572            kind = None
7573            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7574        else:
7575            # Empty kind  https://prestodb.io/docs/current/sql/analyze.html
7576            kind = None
7577            this = self._parse_table_parts()
7578
7579        partition = self._try_parse(self._parse_partition)
7580        if not partition and self._match_texts(self.PARTITION_KEYWORDS):
7581            return self._parse_as_command(start)
7582
7583        # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7584        if self._match_text_seq("WITH", "SYNC", "MODE") or self._match_text_seq(
7585            "WITH", "ASYNC", "MODE"
7586        ):
7587            mode = f"WITH {self._tokens[self._index - 2].text.upper()} MODE"
7588        else:
7589            mode = None
7590
7591        if self._match_texts(self.ANALYZE_EXPRESSION_PARSERS):
7592            inner_expression = self.ANALYZE_EXPRESSION_PARSERS[self._prev.text.upper()](self)
7593
7594        properties = self._parse_properties()
7595        return self.expression(
7596            exp.Analyze,
7597            kind=kind,
7598            this=this,
7599            mode=mode,
7600            partition=partition,
7601            properties=properties,
7602            expression=inner_expression,
7603            options=options,
7604        )
7605
7606    # https://spark.apache.org/docs/3.5.1/sql-ref-syntax-aux-analyze-table.html
7607    def _parse_analyze_statistics(self) -> exp.AnalyzeStatistics:
7608        this = None
7609        kind = self._prev.text.upper()
7610        option = self._prev.text.upper() if self._match_text_seq("DELTA") else None
7611        expressions = []
7612
7613        if not self._match_text_seq("STATISTICS"):
7614            self.raise_error("Expecting token STATISTICS")
7615
7616        if self._match_text_seq("NOSCAN"):
7617            this = "NOSCAN"
7618        elif self._match(TokenType.FOR):
7619            if self._match_text_seq("ALL", "COLUMNS"):
7620                this = "FOR ALL COLUMNS"
7621            if self._match_texts("COLUMNS"):
7622                this = "FOR COLUMNS"
7623                expressions = self._parse_csv(self._parse_column_reference)
7624        elif self._match_text_seq("SAMPLE"):
7625            sample = self._parse_number()
7626            expressions = [
7627                self.expression(
7628                    exp.AnalyzeSample,
7629                    sample=sample,
7630                    kind=self._prev.text.upper() if self._match(TokenType.PERCENT) else None,
7631                )
7632            ]
7633
7634        return self.expression(
7635            exp.AnalyzeStatistics, kind=kind, option=option, this=this, expressions=expressions
7636        )
7637
7638    # https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/ANALYZE.html
7639    def _parse_analyze_validate(self) -> exp.AnalyzeValidate:
7640        kind = None
7641        this = None
7642        expression: t.Optional[exp.Expression] = None
7643        if self._match_text_seq("REF", "UPDATE"):
7644            kind = "REF"
7645            this = "UPDATE"
7646            if self._match_text_seq("SET", "DANGLING", "TO", "NULL"):
7647                this = "UPDATE SET DANGLING TO NULL"
7648        elif self._match_text_seq("STRUCTURE"):
7649            kind = "STRUCTURE"
7650            if self._match_text_seq("CASCADE", "FAST"):
7651                this = "CASCADE FAST"
7652            elif self._match_text_seq("CASCADE", "COMPLETE") and self._match_texts(
7653                ("ONLINE", "OFFLINE")
7654            ):
7655                this = f"CASCADE COMPLETE {self._prev.text.upper()}"
7656                expression = self._parse_into()
7657
7658        return self.expression(exp.AnalyzeValidate, kind=kind, this=this, expression=expression)
7659
7660    def _parse_analyze_columns(self) -> t.Optional[exp.AnalyzeColumns]:
7661        this = self._prev.text.upper()
7662        if self._match_text_seq("COLUMNS"):
7663            return self.expression(exp.AnalyzeColumns, this=f"{this} {self._prev.text.upper()}")
7664        return None
7665
7666    def _parse_analyze_delete(self) -> t.Optional[exp.AnalyzeDelete]:
7667        kind = self._prev.text.upper() if self._match_text_seq("SYSTEM") else None
7668        if self._match_text_seq("STATISTICS"):
7669            return self.expression(exp.AnalyzeDelete, kind=kind)
7670        return None
7671
7672    def _parse_analyze_list(self) -> t.Optional[exp.AnalyzeListChainedRows]:
7673        if self._match_text_seq("CHAINED", "ROWS"):
7674            return self.expression(exp.AnalyzeListChainedRows, expression=self._parse_into())
7675        return None
7676
7677    # https://dev.mysql.com/doc/refman/8.4/en/analyze-table.html
7678    def _parse_analyze_histogram(self) -> exp.AnalyzeHistogram:
7679        this = self._prev.text.upper()
7680        expression: t.Optional[exp.Expression] = None
7681        expressions = []
7682        update_options = None
7683
7684        if self._match_text_seq("HISTOGRAM", "ON"):
7685            expressions = self._parse_csv(self._parse_column_reference)
7686            with_expressions = []
7687            while self._match(TokenType.WITH):
7688                # https://docs.starrocks.io/docs/sql-reference/sql-statements/cbo_stats/ANALYZE_TABLE/
7689                if self._match_texts(("SYNC", "ASYNC")):
7690                    if self._match_text_seq("MODE", advance=False):
7691                        with_expressions.append(f"{self._prev.text.upper()} MODE")
7692                        self._advance()
7693                else:
7694                    buckets = self._parse_number()
7695                    if self._match_text_seq("BUCKETS"):
7696                        with_expressions.append(f"{buckets} BUCKETS")
7697            if with_expressions:
7698                expression = self.expression(exp.AnalyzeWith, expressions=with_expressions)
7699
7700            if self._match_texts(("MANUAL", "AUTO")) and self._match(
7701                TokenType.UPDATE, advance=False
7702            ):
7703                update_options = self._prev.text.upper()
7704                self._advance()
7705            elif self._match_text_seq("USING", "DATA"):
7706                expression = self.expression(exp.UsingData, this=self._parse_string())
7707
7708        return self.expression(
7709            exp.AnalyzeHistogram,
7710            this=this,
7711            expressions=expressions,
7712            expression=expression,
7713            update_options=update_options,
7714        )
7715
7716    def _parse_merge(self) -> exp.Merge:
7717        self._match(TokenType.INTO)
7718        target = self._parse_table()
7719
7720        if target and self._match(TokenType.ALIAS, advance=False):
7721            target.set("alias", self._parse_table_alias())
7722
7723        self._match(TokenType.USING)
7724        using = self._parse_table()
7725
7726        self._match(TokenType.ON)
7727        on = self._parse_assignment()
7728
7729        return self.expression(
7730            exp.Merge,
7731            this=target,
7732            using=using,
7733            on=on,
7734            whens=self._parse_when_matched(),
7735            returning=self._parse_returning(),
7736        )
7737
7738    def _parse_when_matched(self) -> exp.Whens:
7739        whens = []
7740
7741        while self._match(TokenType.WHEN):
7742            matched = not self._match(TokenType.NOT)
7743            self._match_text_seq("MATCHED")
7744            source = (
7745                False
7746                if self._match_text_seq("BY", "TARGET")
7747                else self._match_text_seq("BY", "SOURCE")
7748            )
7749            condition = self._parse_assignment() if self._match(TokenType.AND) else None
7750
7751            self._match(TokenType.THEN)
7752
7753            if self._match(TokenType.INSERT):
7754                this = self._parse_star()
7755                if this:
7756                    then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=this)
7757                else:
7758                    then = self.expression(
7759                        exp.Insert,
7760                        this=exp.var("ROW")
7761                        if self._match_text_seq("ROW")
7762                        else self._parse_value(values=False),
7763                        expression=self._match_text_seq("VALUES") and self._parse_value(),
7764                    )
7765            elif self._match(TokenType.UPDATE):
7766                expressions = self._parse_star()
7767                if expressions:
7768                    then = self.expression(exp.Update, expressions=expressions)
7769                else:
7770                    then = self.expression(
7771                        exp.Update,
7772                        expressions=self._match(TokenType.SET)
7773                        and self._parse_csv(self._parse_equality),
7774                    )
7775            elif self._match(TokenType.DELETE):
7776                then = self.expression(exp.Var, this=self._prev.text)
7777            else:
7778                then = self._parse_var_from_options(self.CONFLICT_ACTIONS)
7779
7780            whens.append(
7781                self.expression(
7782                    exp.When,
7783                    matched=matched,
7784                    source=source,
7785                    condition=condition,
7786                    then=then,
7787                )
7788            )
7789        return self.expression(exp.Whens, expressions=whens)
7790
7791    def _parse_show(self) -> t.Optional[exp.Expression]:
7792        parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE)
7793        if parser:
7794            return parser(self)
7795        return self._parse_as_command(self._prev)
7796
7797    def _parse_set_item_assignment(
7798        self, kind: t.Optional[str] = None
7799    ) -> t.Optional[exp.Expression]:
7800        index = self._index
7801
7802        if kind in ("GLOBAL", "SESSION") and self._match_text_seq("TRANSACTION"):
7803            return self._parse_set_transaction(global_=kind == "GLOBAL")
7804
7805        left = self._parse_primary() or self._parse_column()
7806        assignment_delimiter = self._match_texts(("=", "TO"))
7807
7808        if not left or (self.SET_REQUIRES_ASSIGNMENT_DELIMITER and not assignment_delimiter):
7809            self._retreat(index)
7810            return None
7811
7812        right = self._parse_statement() or self._parse_id_var()
7813        if isinstance(right, (exp.Column, exp.Identifier)):
7814            right = exp.var(right.name)
7815
7816        this = self.expression(exp.EQ, this=left, expression=right)
7817        return self.expression(exp.SetItem, this=this, kind=kind)
7818
7819    def _parse_set_transaction(self, global_: bool = False) -> exp.Expression:
7820        self._match_text_seq("TRANSACTION")
7821        characteristics = self._parse_csv(
7822            lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS)
7823        )
7824        return self.expression(
7825            exp.SetItem,
7826            expressions=characteristics,
7827            kind="TRANSACTION",
7828            **{"global": global_},  # type: ignore
7829        )
7830
7831    def _parse_set_item(self) -> t.Optional[exp.Expression]:
7832        parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE)
7833        return parser(self) if parser else self._parse_set_item_assignment(kind=None)
7834
7835    def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command:
7836        index = self._index
7837        set_ = self.expression(
7838            exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag
7839        )
7840
7841        if self._curr:
7842            self._retreat(index)
7843            return self._parse_as_command(self._prev)
7844
7845        return set_
7846
7847    def _parse_var_from_options(
7848        self, options: OPTIONS_TYPE, raise_unmatched: bool = True
7849    ) -> t.Optional[exp.Var]:
7850        start = self._curr
7851        if not start:
7852            return None
7853
7854        option = start.text.upper()
7855        continuations = options.get(option)
7856
7857        index = self._index
7858        self._advance()
7859        for keywords in continuations or []:
7860            if isinstance(keywords, str):
7861                keywords = (keywords,)
7862
7863            if self._match_text_seq(*keywords):
7864                option = f"{option} {' '.join(keywords)}"
7865                break
7866        else:
7867            if continuations or continuations is None:
7868                if raise_unmatched:
7869                    self.raise_error(f"Unknown option {option}")
7870
7871                self._retreat(index)
7872                return None
7873
7874        return exp.var(option)
7875
7876    def _parse_as_command(self, start: Token) -> exp.Command:
7877        while self._curr:
7878            self._advance()
7879        text = self._find_sql(start, self._prev)
7880        size = len(start.text)
7881        self._warn_unsupported()
7882        return exp.Command(this=text[:size], expression=text[size:])
7883
7884    def _parse_dict_property(self, this: str) -> exp.DictProperty:
7885        settings = []
7886
7887        self._match_l_paren()
7888        kind = self._parse_id_var()
7889
7890        if self._match(TokenType.L_PAREN):
7891            while True:
7892                key = self._parse_id_var()
7893                value = self._parse_primary()
7894                if not key and value is None:
7895                    break
7896                settings.append(self.expression(exp.DictSubProperty, this=key, value=value))
7897            self._match(TokenType.R_PAREN)
7898
7899        self._match_r_paren()
7900
7901        return self.expression(
7902            exp.DictProperty,
7903            this=this,
7904            kind=kind.this if kind else None,
7905            settings=settings,
7906        )
7907
7908    def _parse_dict_range(self, this: str) -> exp.DictRange:
7909        self._match_l_paren()
7910        has_min = self._match_text_seq("MIN")
7911        if has_min:
7912            min = self._parse_var() or self._parse_primary()
7913            self._match_text_seq("MAX")
7914            max = self._parse_var() or self._parse_primary()
7915        else:
7916            max = self._parse_var() or self._parse_primary()
7917            min = exp.Literal.number(0)
7918        self._match_r_paren()
7919        return self.expression(exp.DictRange, this=this, min=min, max=max)
7920
7921    def _parse_comprehension(
7922        self, this: t.Optional[exp.Expression]
7923    ) -> t.Optional[exp.Comprehension]:
7924        index = self._index
7925        expression = self._parse_column()
7926        if not self._match(TokenType.IN):
7927            self._retreat(index - 1)
7928            return None
7929        iterator = self._parse_column()
7930        condition = self._parse_assignment() if self._match_text_seq("IF") else None
7931        return self.expression(
7932            exp.Comprehension,
7933            this=this,
7934            expression=expression,
7935            iterator=iterator,
7936            condition=condition,
7937        )
7938
7939    def _parse_heredoc(self) -> t.Optional[exp.Heredoc]:
7940        if self._match(TokenType.HEREDOC_STRING):
7941            return self.expression(exp.Heredoc, this=self._prev.text)
7942
7943        if not self._match_text_seq("$"):
7944            return None
7945
7946        tags = ["$"]
7947        tag_text = None
7948
7949        if self._is_connected():
7950            self._advance()
7951            tags.append(self._prev.text.upper())
7952        else:
7953            self.raise_error("No closing $ found")
7954
7955        if tags[-1] != "$":
7956            if self._is_connected() and self._match_text_seq("$"):
7957                tag_text = tags[-1]
7958                tags.append("$")
7959            else:
7960                self.raise_error("No closing $ found")
7961
7962        heredoc_start = self._curr
7963
7964        while self._curr:
7965            if self._match_text_seq(*tags, advance=False):
7966                this = self._find_sql(heredoc_start, self._prev)
7967                self._advance(len(tags))
7968                return self.expression(exp.Heredoc, this=this, tag=tag_text)
7969
7970            self._advance()
7971
7972        self.raise_error(f"No closing {''.join(tags)} found")
7973        return None
7974
7975    def _find_parser(
7976        self, parsers: t.Dict[str, t.Callable], trie: t.Dict
7977    ) -> t.Optional[t.Callable]:
7978        if not self._curr:
7979            return None
7980
7981        index = self._index
7982        this = []
7983        while True:
7984            # The current token might be multiple words
7985            curr = self._curr.text.upper()
7986            key = curr.split(" ")
7987            this.append(curr)
7988
7989            self._advance()
7990            result, trie = in_trie(trie, key)
7991            if result == TrieResult.FAILED:
7992                break
7993
7994            if result == TrieResult.EXISTS:
7995                subparser = parsers[" ".join(this)]
7996                return subparser
7997
7998        self._retreat(index)
7999        return None
8000
8001    def _match(self, token_type, advance=True, expression=None):
8002        if not self._curr:
8003            return None
8004
8005        if self._curr.token_type == token_type:
8006            if advance:
8007                self._advance()
8008            self._add_comments(expression)
8009            return True
8010
8011        return None
8012
8013    def _match_set(self, types, advance=True):
8014        if not self._curr:
8015            return None
8016
8017        if self._curr.token_type in types:
8018            if advance:
8019                self._advance()
8020            return True
8021
8022        return None
8023
8024    def _match_pair(self, token_type_a, token_type_b, advance=True):
8025        if not self._curr or not self._next:
8026            return None
8027
8028        if self._curr.token_type == token_type_a and self._next.token_type == token_type_b:
8029            if advance:
8030                self._advance(2)
8031            return True
8032
8033        return None
8034
8035    def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
8036        if not self._match(TokenType.L_PAREN, expression=expression):
8037            self.raise_error("Expecting (")
8038
8039    def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None:
8040        if not self._match(TokenType.R_PAREN, expression=expression):
8041            self.raise_error("Expecting )")
8042
8043    def _match_texts(self, texts, advance=True):
8044        if (
8045            self._curr
8046            and self._curr.token_type != TokenType.STRING
8047            and self._curr.text.upper() in texts
8048        ):
8049            if advance:
8050                self._advance()
8051            return True
8052        return None
8053
8054    def _match_text_seq(self, *texts, advance=True):
8055        index = self._index
8056        for text in texts:
8057            if (
8058                self._curr
8059                and self._curr.token_type != TokenType.STRING
8060                and self._curr.text.upper() == text
8061            ):
8062                self._advance()
8063            else:
8064                self._retreat(index)
8065                return None
8066
8067        if not advance:
8068            self._retreat(index)
8069
8070        return True
8071
8072    def _replace_lambda(
8073        self, node: t.Optional[exp.Expression], expressions: t.List[exp.Expression]
8074    ) -> t.Optional[exp.Expression]:
8075        if not node:
8076            return node
8077
8078        lambda_types = {e.name: e.args.get("to") or False for e in expressions}
8079
8080        for column in node.find_all(exp.Column):
8081            typ = lambda_types.get(column.parts[0].name)
8082            if typ is not None:
8083                dot_or_id = column.to_dot() if column.table else column.this
8084
8085                if typ:
8086                    dot_or_id = self.expression(
8087                        exp.Cast,
8088                        this=dot_or_id,
8089                        to=typ,
8090                    )
8091
8092                parent = column.parent
8093
8094                while isinstance(parent, exp.Dot):
8095                    if not isinstance(parent.parent, exp.Dot):
8096                        parent.replace(dot_or_id)
8097                        break
8098                    parent = parent.parent
8099                else:
8100                    if column is node:
8101                        node = dot_or_id
8102                    else:
8103                        column.replace(dot_or_id)
8104        return node
8105
8106    def _parse_truncate_table(self) -> t.Optional[exp.TruncateTable] | exp.Expression:
8107        start = self._prev
8108
8109        # Not to be confused with TRUNCATE(number, decimals) function call
8110        if self._match(TokenType.L_PAREN):
8111            self._retreat(self._index - 2)
8112            return self._parse_function()
8113
8114        # Clickhouse supports TRUNCATE DATABASE as well
8115        is_database = self._match(TokenType.DATABASE)
8116
8117        self._match(TokenType.TABLE)
8118
8119        exists = self._parse_exists(not_=False)
8120
8121        expressions = self._parse_csv(
8122            lambda: self._parse_table(schema=True, is_db_reference=is_database)
8123        )
8124
8125        cluster = self._parse_on_property() if self._match(TokenType.ON) else None
8126
8127        if self._match_text_seq("RESTART", "IDENTITY"):
8128            identity = "RESTART"
8129        elif self._match_text_seq("CONTINUE", "IDENTITY"):
8130            identity = "CONTINUE"
8131        else:
8132            identity = None
8133
8134        if self._match_text_seq("CASCADE") or self._match_text_seq("RESTRICT"):
8135            option = self._prev.text
8136        else:
8137            option = None
8138
8139        partition = self._parse_partition()
8140
8141        # Fallback case
8142        if self._curr:
8143            return self._parse_as_command(start)
8144
8145        return self.expression(
8146            exp.TruncateTable,
8147            expressions=expressions,
8148            is_database=is_database,
8149            exists=exists,
8150            cluster=cluster,
8151            identity=identity,
8152            option=option,
8153            partition=partition,
8154        )
8155
8156    def _parse_with_operator(self) -> t.Optional[exp.Expression]:
8157        this = self._parse_ordered(self._parse_opclass)
8158
8159        if not self._match(TokenType.WITH):
8160            return this
8161
8162        op = self._parse_var(any_token=True)
8163
8164        return self.expression(exp.WithOperator, this=this, op=op)
8165
8166    def _parse_wrapped_options(self) -> t.List[t.Optional[exp.Expression]]:
8167        self._match(TokenType.EQ)
8168        self._match(TokenType.L_PAREN)
8169
8170        opts: t.List[t.Optional[exp.Expression]] = []
8171        option: exp.Expression | None
8172        while self._curr and not self._match(TokenType.R_PAREN):
8173            if self._match_text_seq("FORMAT_NAME", "="):
8174                # The FORMAT_NAME can be set to an identifier for Snowflake and T-SQL
8175                option = self._parse_format_name()
8176            else:
8177                option = self._parse_property()
8178
8179            if option is None:
8180                self.raise_error("Unable to parse option")
8181                break
8182
8183            opts.append(option)
8184
8185        return opts
8186
8187    def _parse_copy_parameters(self) -> t.List[exp.CopyParameter]:
8188        sep = TokenType.COMMA if self.dialect.COPY_PARAMS_ARE_CSV else None
8189
8190        options = []
8191        while self._curr and not self._match(TokenType.R_PAREN, advance=False):
8192            option = self._parse_var(any_token=True)
8193            prev = self._prev.text.upper()
8194
8195            # Different dialects might separate options and values by white space, "=" and "AS"
8196            self._match(TokenType.EQ)
8197            self._match(TokenType.ALIAS)
8198
8199            param = self.expression(exp.CopyParameter, this=option)
8200
8201            if prev in self.COPY_INTO_VARLEN_OPTIONS and self._match(
8202                TokenType.L_PAREN, advance=False
8203            ):
8204                # Snowflake FILE_FORMAT case, Databricks COPY & FORMAT options
8205                param.set("expressions", self._parse_wrapped_options())
8206            elif prev == "FILE_FORMAT":
8207                # T-SQL's external file format case
8208                param.set("expression", self._parse_field())
8209            else:
8210                param.set("expression", self._parse_unquoted_field())
8211
8212            options.append(param)
8213            self._match(sep)
8214
8215        return options
8216
8217    def _parse_credentials(self) -> t.Optional[exp.Credentials]:
8218        expr = self.expression(exp.Credentials)
8219
8220        if self._match_text_seq("STORAGE_INTEGRATION", "="):
8221            expr.set("storage", self._parse_field())
8222        if self._match_text_seq("CREDENTIALS"):
8223            # Snowflake case: CREDENTIALS = (...), Redshift case: CREDENTIALS <string>
8224            creds = (
8225                self._parse_wrapped_options() if self._match(TokenType.EQ) else self._parse_field()
8226            )
8227            expr.set("credentials", creds)
8228        if self._match_text_seq("ENCRYPTION"):
8229            expr.set("encryption", self._parse_wrapped_options())
8230        if self._match_text_seq("IAM_ROLE"):
8231            expr.set("iam_role", self._parse_field())
8232        if self._match_text_seq("REGION"):
8233            expr.set("region", self._parse_field())
8234
8235        return expr
8236
8237    def _parse_file_location(self) -> t.Optional[exp.Expression]:
8238        return self._parse_field()
8239
8240    def _parse_copy(self) -> exp.Copy | exp.Command:
8241        start = self._prev
8242
8243        self._match(TokenType.INTO)
8244
8245        this = (
8246            self._parse_select(nested=True, parse_subquery_alias=False)
8247            if self._match(TokenType.L_PAREN, advance=False)
8248            else self._parse_table(schema=True)
8249        )
8250
8251        kind = self._match(TokenType.FROM) or not self._match_text_seq("TO")
8252
8253        files = self._parse_csv(self._parse_file_location)
8254        credentials = self._parse_credentials()
8255
8256        self._match_text_seq("WITH")
8257
8258        params = self._parse_wrapped(self._parse_copy_parameters, optional=True)
8259
8260        # Fallback case
8261        if self._curr:
8262            return self._parse_as_command(start)
8263
8264        return self.expression(
8265            exp.Copy,
8266            this=this,
8267            kind=kind,
8268            credentials=credentials,
8269            files=files,
8270            params=params,
8271        )
8272
8273    def _parse_normalize(self) -> exp.Normalize:
8274        return self.expression(
8275            exp.Normalize,
8276            this=self._parse_bitwise(),
8277            form=self._match(TokenType.COMMA) and self._parse_var(),
8278        )
8279
8280    def _parse_ceil_floor(self, expr_type: t.Type[TCeilFloor]) -> TCeilFloor:
8281        args = self._parse_csv(lambda: self._parse_lambda())
8282
8283        this = seq_get(args, 0)
8284        decimals = seq_get(args, 1)
8285
8286        return expr_type(
8287            this=this, decimals=decimals, to=self._match_text_seq("TO") and self._parse_var()
8288        )
8289
8290    def _parse_star_ops(self) -> t.Optional[exp.Expression]:
8291        star_token = self._prev
8292
8293        if self._match_text_seq("COLUMNS", "(", advance=False):
8294            this = self._parse_function()
8295            if isinstance(this, exp.Columns):
8296                this.set("unpack", True)
8297            return this
8298
8299        return self.expression(
8300            exp.Star,
8301            **{  # type: ignore
8302                "except": self._parse_star_op("EXCEPT", "EXCLUDE"),
8303                "replace": self._parse_star_op("REPLACE"),
8304                "rename": self._parse_star_op("RENAME"),
8305            },
8306        ).update_positions(star_token)
8307
8308    def _parse_grant_privilege(self) -> t.Optional[exp.GrantPrivilege]:
8309        privilege_parts = []
8310
8311        # Keep consuming consecutive keywords until comma (end of this privilege) or ON
8312        # (end of privilege list) or L_PAREN (start of column list) are met
8313        while self._curr and not self._match_set(self.PRIVILEGE_FOLLOW_TOKENS, advance=False):
8314            privilege_parts.append(self._curr.text.upper())
8315            self._advance()
8316
8317        this = exp.var(" ".join(privilege_parts))
8318        expressions = (
8319            self._parse_wrapped_csv(self._parse_column)
8320            if self._match(TokenType.L_PAREN, advance=False)
8321            else None
8322        )
8323
8324        return self.expression(exp.GrantPrivilege, this=this, expressions=expressions)
8325
8326    def _parse_grant_principal(self) -> t.Optional[exp.GrantPrincipal]:
8327        kind = self._match_texts(("ROLE", "GROUP")) and self._prev.text.upper()
8328        principal = self._parse_id_var()
8329
8330        if not principal:
8331            return None
8332
8333        return self.expression(exp.GrantPrincipal, this=principal, kind=kind)
8334
8335    def _parse_grant(self) -> exp.Grant | exp.Command:
8336        start = self._prev
8337
8338        privileges = self._parse_csv(self._parse_grant_privilege)
8339
8340        self._match(TokenType.ON)
8341        kind = self._match_set(self.CREATABLES) and self._prev.text.upper()
8342
8343        # Attempt to parse the securable e.g. MySQL allows names
8344        # such as "foo.*", "*.*" which are not easily parseable yet
8345        securable = self._try_parse(self._parse_table_parts)
8346
8347        if not securable or not self._match_text_seq("TO"):
8348            return self._parse_as_command(start)
8349
8350        principals = self._parse_csv(self._parse_grant_principal)
8351
8352        grant_option = self._match_text_seq("WITH", "GRANT", "OPTION")
8353
8354        if self._curr:
8355            return self._parse_as_command(start)
8356
8357        return self.expression(
8358            exp.Grant,
8359            privileges=privileges,
8360            kind=kind,
8361            securable=securable,
8362            principals=principals,
8363            grant_option=grant_option,
8364        )
8365
8366    def _parse_overlay(self) -> exp.Overlay:
8367        return self.expression(
8368            exp.Overlay,
8369            **{  # type: ignore
8370                "this": self._parse_bitwise(),
8371                "expression": self._match_text_seq("PLACING") and self._parse_bitwise(),
8372                "from": self._match_text_seq("FROM") and self._parse_bitwise(),
8373                "for": self._match_text_seq("FOR") and self._parse_bitwise(),
8374            },
8375        )
8376
8377    def _parse_format_name(self) -> exp.Property:
8378        # Note: Although not specified in the docs, Snowflake does accept a string/identifier
8379        # for FILE_FORMAT = <format_name>
8380        return self.expression(
8381            exp.Property,
8382            this=exp.var("FORMAT_NAME"),
8383            value=self._parse_string() or self._parse_table_parts(),
8384        )
8385
8386    def _parse_max_min_by(self, expr_type: t.Type[exp.AggFunc]) -> exp.AggFunc:
8387        args: t.List[exp.Expression] = []
8388
8389        if self._match(TokenType.DISTINCT):
8390            args.append(self.expression(exp.Distinct, expressions=[self._parse_assignment()]))
8391            self._match(TokenType.COMMA)
8392
8393        args.extend(self._parse_csv(self._parse_assignment))
8394
8395        return self.expression(
8396            expr_type, this=seq_get(args, 0), expression=seq_get(args, 1), count=seq_get(args, 2)
8397        )
8398
8399    def _identifier_expression(
8400        self, token: t.Optional[Token] = None, **kwargs: t.Any
8401    ) -> exp.Identifier:
8402        token = token or self._prev
8403        expression = self.expression(exp.Identifier, this=token.text, **kwargs)
8404        expression.update_positions(token)
8405        return expression
8406
8407    def _build_pipe_cte(
8408        self,
8409        query: exp.Query,
8410        expressions: t.List[exp.Expression],
8411        alias_cte: t.Optional[exp.TableAlias] = None,
8412    ) -> exp.Select:
8413        new_cte: t.Optional[t.Union[str, exp.TableAlias]]
8414        if alias_cte:
8415            new_cte = alias_cte
8416        else:
8417            self._pipe_cte_counter += 1
8418            new_cte = f"__tmp{self._pipe_cte_counter}"
8419
8420        with_ = query.args.get("with")
8421        ctes = with_.pop() if with_ else None
8422
8423        new_select = exp.select(*expressions, copy=False).from_(new_cte, copy=False)
8424        if ctes:
8425            new_select.set("with", ctes)
8426
8427        return new_select.with_(new_cte, as_=query, copy=False)
8428
8429    def _parse_pipe_syntax_select(self, query: exp.Select) -> exp.Select:
8430        select = self._parse_select(consume_pipe=False)
8431        if not select:
8432            return query
8433
8434        return self._build_pipe_cte(
8435            query=query.select(*select.expressions, append=False), expressions=[exp.Star()]
8436        )
8437
8438    def _parse_pipe_syntax_limit(self, query: exp.Select) -> exp.Select:
8439        limit = self._parse_limit()
8440        offset = self._parse_offset()
8441        if limit:
8442            curr_limit = query.args.get("limit", limit)
8443            if curr_limit.expression.to_py() >= limit.expression.to_py():
8444                query.limit(limit, copy=False)
8445        if offset:
8446            curr_offset = query.args.get("offset")
8447            curr_offset = curr_offset.expression.to_py() if curr_offset else 0
8448            query.offset(exp.Literal.number(curr_offset + offset.expression.to_py()), copy=False)
8449
8450        return query
8451
8452    def _parse_pipe_syntax_aggregate_fields(self) -> t.Optional[exp.Expression]:
8453        this = self._parse_assignment()
8454        if self._match_text_seq("GROUP", "AND", advance=False):
8455            return this
8456
8457        this = self._parse_alias(this)
8458
8459        if self._match_set((TokenType.ASC, TokenType.DESC), advance=False):
8460            return self._parse_ordered(lambda: this)
8461
8462        return this
8463
8464    def _parse_pipe_syntax_aggregate_group_order_by(
8465        self, query: exp.Select, group_by_exists: bool = True
8466    ) -> exp.Select:
8467        expr = self._parse_csv(self._parse_pipe_syntax_aggregate_fields)
8468        aggregates_or_groups, orders = [], []
8469        for element in expr:
8470            if isinstance(element, exp.Ordered):
8471                this = element.this
8472                if isinstance(this, exp.Alias):
8473                    element.set("this", this.args["alias"])
8474                orders.append(element)
8475            else:
8476                this = element
8477            aggregates_or_groups.append(this)
8478
8479        if group_by_exists:
8480            query.select(*aggregates_or_groups, copy=False).group_by(
8481                *[projection.args.get("alias", projection) for projection in aggregates_or_groups],
8482                copy=False,
8483            )
8484        else:
8485            query.select(*aggregates_or_groups, append=False, copy=False)
8486
8487        if orders:
8488            return query.order_by(*orders, append=False, copy=False)
8489
8490        return query
8491
8492    def _parse_pipe_syntax_aggregate(self, query: exp.Select) -> exp.Select:
8493        self._match_text_seq("AGGREGATE")
8494        query = self._parse_pipe_syntax_aggregate_group_order_by(query, group_by_exists=False)
8495
8496        if self._match(TokenType.GROUP_BY) or (
8497            self._match_text_seq("GROUP", "AND") and self._match(TokenType.ORDER_BY)
8498        ):
8499            query = self._parse_pipe_syntax_aggregate_group_order_by(query)
8500
8501        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8502
8503    def _parse_pipe_syntax_set_operator(self, query: exp.Query) -> t.Optional[exp.Query]:
8504        first_setop = self.parse_set_operation(this=query)
8505        if not first_setop:
8506            return None
8507
8508        def _parse_and_unwrap_query() -> t.Optional[exp.Select]:
8509            expr = self._parse_paren()
8510            return expr.assert_is(exp.Subquery).unnest() if expr else None
8511
8512        first_setop.this.pop()
8513
8514        setops = [
8515            first_setop.expression.pop().assert_is(exp.Subquery).unnest(),
8516            *self._parse_csv(_parse_and_unwrap_query),
8517        ]
8518
8519        query = self._build_pipe_cte(query=query, expressions=[exp.Star()])
8520        with_ = query.args.get("with")
8521        ctes = with_.pop() if with_ else None
8522
8523        if isinstance(first_setop, exp.Union):
8524            query = query.union(*setops, copy=False, **first_setop.args)
8525        elif isinstance(first_setop, exp.Except):
8526            query = query.except_(*setops, copy=False, **first_setop.args)
8527        else:
8528            query = query.intersect(*setops, copy=False, **first_setop.args)
8529
8530        query.set("with", ctes)
8531
8532        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8533
8534    def _parse_pipe_syntax_join(self, query: exp.Query) -> t.Optional[exp.Query]:
8535        join = self._parse_join()
8536        if not join:
8537            return None
8538
8539        if isinstance(query, exp.Select):
8540            return query.join(join, copy=False)
8541
8542        return query
8543
8544    def _parse_pipe_syntax_pivot(self, query: exp.Select) -> exp.Select:
8545        pivots = self._parse_pivots()
8546        if not pivots:
8547            return query
8548
8549        from_ = query.args.get("from")
8550        if from_:
8551            from_.this.set("pivots", pivots)
8552
8553        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8554
8555    def _parse_pipe_syntax_extend(self, query: exp.Select) -> exp.Select:
8556        self._match_text_seq("EXTEND")
8557        query.select(*[exp.Star(), *self._parse_expressions()], append=False, copy=False)
8558        return self._build_pipe_cte(query=query, expressions=[exp.Star()])
8559
8560    def _parse_pipe_syntax_tablesample(self, query: exp.Select) -> exp.Select:
8561        sample = self._parse_table_sample()
8562
8563        with_ = query.args.get("with")
8564        if with_:
8565            with_.expressions[-1].this.set("sample", sample)
8566        else:
8567            query.set("sample", sample)
8568
8569        return query
8570
8571    def _parse_pipe_syntax_query(self, query: exp.Query) -> t.Optional[exp.Query]:
8572        if isinstance(query, exp.Subquery):
8573            query = exp.select("*").from_(query, copy=False)
8574
8575        if not query.args.get("from"):
8576            query = exp.select("*").from_(query.subquery(copy=False), copy=False)
8577
8578        while self._match(TokenType.PIPE_GT):
8579            start = self._curr
8580            parser = self.PIPE_SYNTAX_TRANSFORM_PARSERS.get(self._curr.text.upper())
8581            if not parser:
8582                # The set operators (UNION, etc) and the JOIN operator have a few common starting
8583                # keywords, making it tricky to disambiguate them without lookahead. The approach
8584                # here is to try and parse a set operation and if that fails, then try to parse a
8585                # join operator. If that fails as well, then the operator is not supported.
8586                parsed_query = self._parse_pipe_syntax_set_operator(query)
8587                parsed_query = parsed_query or self._parse_pipe_syntax_join(query)
8588                if not parsed_query:
8589                    self._retreat(start)
8590                    self.raise_error(f"Unsupported pipe syntax operator: '{start.text.upper()}'.")
8591                    break
8592                query = parsed_query
8593            else:
8594                query = parser(self, query)
8595
8596        return query

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)
1553    def __init__(
1554        self,
1555        error_level: t.Optional[ErrorLevel] = None,
1556        error_message_context: int = 100,
1557        max_errors: int = 3,
1558        dialect: DialectType = None,
1559    ):
1560        from sqlglot.dialects import Dialect
1561
1562        self.error_level = error_level or ErrorLevel.IMMEDIATE
1563        self.error_message_context = error_message_context
1564        self.max_errors = max_errors
1565        self.dialect = Dialect.get_or_raise(dialect)
1566        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_CONCAT_AGG': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayConcatAgg'>>, '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_FIRST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayFirst'>>, 'ARRAY_INTERSECT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayIntersect'>>, 'ARRAY_INTERSECTION': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayIntersect'>>, 'ARRAY_LAST': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayLast'>>, 'ARRAY_OVERLAPS': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayOverlaps'>>, 'ARRAY_REMOVE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayRemove'>>, 'ARRAY_REVERSE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArrayReverse'>>, '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_SLICE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.ArraySlice'>>, '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_TIMESTAMP_L_T_Z': <bound method Func.from_arg_list of <class 'sqlglot.expressions.CurrentTimestampLTZ'>>, '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'>>, 'ENDS_WITH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.EndsWith'>>, 'ENDSWITH': <bound method Func.from_arg_list of <class 'sqlglot.expressions.EndsWith'>>, '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'>>, 'REPLACE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Replace'>>, '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'>>, 'SPACE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Space'>>, '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'>>, 'ST_DISTANCE': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StDistance'>>, 'ST_POINT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StPoint'>>, 'ST_MAKEPOINT': <bound method Func.from_arg_list of <class 'sqlglot.expressions.StPoint'>>, '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'>>, 'STRTOK_TO_ARRAY': <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'>>, 'SUBSTRING_INDEX': <bound method Func.from_arg_list of <class 'sqlglot.expressions.SubstringIndex'>>, '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'>>, 'TYPEOF': <bound method Func.from_arg_list of <class 'sqlglot.expressions.Typeof'>>, '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.NESTED: 'NESTED'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.UNION: 'UNION'>}
NESTED_TYPE_TOKENS = {<TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.RANGE: 'RANGE'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.MAP: 'MAP'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NESTED: 'NESTED'>, <TokenType.LIST: 'LIST'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.UNION: 'UNION'>}
ENUM_TYPE_TOKENS = {<TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.ENUM: 'ENUM'>}
AGGREGATE_TYPE_TOKENS = {<TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>}
TYPE_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TIME: 'TIME'>, <TokenType.UINT256: 'UINT256'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.BIT: 'BIT'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.JSON: 'JSON'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CHAR: 'CHAR'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.RANGE: 'RANGE'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.UNION: 'UNION'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.IPV4: 'IPV4'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.POINT: 'POINT'>, <TokenType.RING: 'RING'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.NAME: 'NAME'>}
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.HASH: 'HASH'>, <TokenType.L_BRACE: 'L_BRACE'>, <TokenType.MOD: 'MOD'>, <TokenType.L_PAREN: 'L_PAREN'>, <TokenType.R_PAREN: 'R_PAREN'>, <TokenType.PLUS: 'PLUS'>, <TokenType.SELECT: 'SELECT'>, <TokenType.R_BRACKET: 'R_BRACKET'>, <TokenType.LT: 'LT'>, <TokenType.NOT: 'NOT'>, <TokenType.SLASH: 'SLASH'>, <TokenType.COMMA: 'COMMA'>, <TokenType.GT: 'GT'>, <TokenType.DOT: 'DOT'>, <TokenType.SEMICOLON: 'SEMICOLON'>, <TokenType.L_BRACKET: 'L_BRACKET'>, <TokenType.EQ: 'EQ'>, <TokenType.COLON: 'COLON'>, <TokenType.PIPE: 'PIPE'>, <TokenType.CARET: 'CARET'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.R_BRACE: 'R_BRACE'>, <TokenType.BACKSLASH: 'BACKSLASH'>, <TokenType.PLACEHOLDER: 'PLACEHOLDER'>, <TokenType.PARAMETER: 'PARAMETER'>, <TokenType.DASH: 'DASH'>, <TokenType.STAR: 'STAR'>, <TokenType.TILDA: 'TILDA'>, <TokenType.AMP: 'AMP'>}
DB_CREATABLES = {<TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.TABLE: 'TABLE'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.SINK: 'SINK'>, <TokenType.STAGE: 'STAGE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.VIEW: 'VIEW'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.TAG: 'TAG'>, <TokenType.MODEL: 'MODEL'>}
CREATABLES = {<TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.STAGE: 'STAGE'>, <TokenType.VIEW: 'VIEW'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.INDEX: 'INDEX'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.MODEL: 'MODEL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.TABLE: 'TABLE'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.SINK: 'SINK'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.TAG: 'TAG'>}
ALTERABLES = {<TokenType.VIEW: 'VIEW'>, <TokenType.INDEX: 'INDEX'>, <TokenType.TABLE: 'TABLE'>}
ID_VAR_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.SEMI: 'SEMI'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.ANTI: 'ANTI'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.FULL: 'FULL'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.ASOF: 'ASOF'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.APPLY: 'APPLY'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
TABLE_ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.NAME: 'NAME'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.SEMI: 'SEMI'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.ANTI: 'ANTI'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.FULL: 'FULL'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.ASOF: 'ASOF'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.APPLY: 'APPLY'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
COLON_PLACEHOLDER_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.SEMI: 'SEMI'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.ANTI: 'ANTI'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.FULL: 'FULL'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.ASOF: 'ASOF'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.APPLY: 'APPLY'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
ARRAY_CONSTRUCTORS = {'ARRAY': <class 'sqlglot.expressions.Array'>, 'LIST': <class 'sqlglot.expressions.List'>}
COMMENT_TABLE_ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
UPDATE_ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
TRIM_TYPES = {'TRAILING', 'BOTH', 'LEADING'}
FUNC_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TIME: 'TIME'>, <TokenType.PRIMARY_KEY: 'PRIMARY_KEY'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.BIT: 'BIT'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.ILIKE: 'ILIKE'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.ANY: 'ANY'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.JSON: 'JSON'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.INSERT: 'INSERT'>, <TokenType.ROW: 'ROW'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.RLIKE: 'RLIKE'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.UNION: 'UNION'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.FIRST: 'FIRST'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.IPV4: 'IPV4'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.FILTER: 'FILTER'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.XOR: 'XOR'>, <TokenType.POINT: 'POINT'>, <TokenType.RING: 'RING'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.LIKE: 'LIKE'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.GLOB: 'GLOB'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>}
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.TIMESTAMP: 'TIMESTAMP'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>}
SET_OPERATIONS = {<TokenType.EXCEPT: 'EXCEPT'>, <TokenType.UNION: 'UNION'>, <TokenType.INTERSECT: 'INTERSECT'>}
JOIN_METHODS = {<TokenType.POSITIONAL: 'POSITIONAL'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.ASOF: 'ASOF'>}
JOIN_SIDES = {<TokenType.LEFT: 'LEFT'>, <TokenType.FULL: 'FULL'>, <TokenType.RIGHT: 'RIGHT'>}
JOIN_KINDS = {<TokenType.SEMI: 'SEMI'>, <TokenType.OUTER: 'OUTER'>, <TokenType.INNER: 'INNER'>, <TokenType.ANTI: 'ANTI'>, <TokenType.STRAIGHT_JOIN: 'STRAIGHT_JOIN'>, <TokenType.CROSS: 'CROSS'>}
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>>}
PIPE_SYNTAX_TRANSFORM_PARSERS = {'AGGREGATE': <function Parser.<lambda>>, 'AS': <function Parser.<lambda>>, 'EXTEND': <function Parser.<lambda>>, 'LIMIT': <function Parser.<lambda>>, 'ORDER BY': <function Parser.<lambda>>, 'PIVOT': <function Parser.<lambda>>, 'SELECT': <function Parser.<lambda>>, 'TABLESAMPLE': <function Parser.<lambda>>, 'UNPIVOT': <function Parser.<lambda>>, 'WHERE': <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 = {'WATERMARK', 'EXCLUDE', 'FOREIGN KEY', 'PERIOD', 'TRUNCATE', 'LIKE', 'PRIMARY KEY', 'UNIQUE', 'BUCKET', 'CHECK'}
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.WITH: 'WITH'>, <TokenType.SELECT: 'SELECT'>, <TokenType.L_PAREN: 'L_PAREN'>}
PRE_VOLATILE_TOKENS = {<TokenType.REPLACE: 'REPLACE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.CREATE: 'CREATE'>}
TRANSACTION_KIND = {'IMMEDIATE', 'EXCLUSIVE', 'DEFERRED'}
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 = {'REPLACE', 'ABORT', 'FAIL', 'IGNORE', 'ROLLBACK'}
CLONE_KEYWORDS = {'COPY', 'CLONE'}
HISTORICAL_DATA_PREFIX = {'END', 'AT', 'BEFORE'}
HISTORICAL_DATA_KIND = {'STREAM', 'TIMESTAMP', 'OFFSET', 'VERSION', 'STATEMENT'}
OPCLASS_FOLLOW_KEYWORDS = {'ASC', 'NULLS', 'WITH', 'DESC'}
OPTYPE_FOLLOW_TOKENS = {<TokenType.COMMA: 'COMMA'>, <TokenType.R_PAREN: 'R_PAREN'>}
TABLE_INDEX_HINT_TOKENS = {<TokenType.FORCE: 'FORCE'>, <TokenType.IGNORE: 'IGNORE'>, <TokenType.USE: 'USE'>}
VIEW_ATTRIBUTES = {'SCHEMABINDING', 'VIEW_METADATA', 'ENCRYPTION'}
WINDOW_ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.SEMI: 'SEMI'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.ANTI: 'ANTI'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.FULL: 'FULL'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.ASOF: 'ASOF'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.APPLY: 'APPLY'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.NAME: 'NAME'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
WINDOW_BEFORE_PAREN_TOKENS = {<TokenType.OVER: 'OVER'>}
WINDOW_SIDES = {'FOLLOWING', 'PRECEDING'}
JSON_KEY_VALUE_SEPARATOR_TOKENS = {<TokenType.COMMA: 'COMMA'>, <TokenType.IS: 'IS'>, <TokenType.COLON: 'COLON'>}
FETCH_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.SEMI: 'SEMI'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.ANTI: 'ANTI'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.FULL: 'FULL'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.LEFT: 'LEFT'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.WINDOW: 'WINDOW'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.ASOF: 'ASOF'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.NATURAL: 'NATURAL'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.RIGHT: 'RIGHT'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.APPLY: 'APPLY'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.NAME: 'NAME'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
ADD_CONSTRAINT_TOKENS = {<TokenType.KEY: 'KEY'>, <TokenType.PRIMARY_KEY: 'PRIMARY_KEY'>, <TokenType.INDEX: 'INDEX'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.UNIQUE: 'UNIQUE'>}
DISTINCT_TOKENS = {<TokenType.DISTINCT: 'DISTINCT'>}
NULL_TOKENS = {<TokenType.NULL: 'NULL'>}
UNNEST_OFFSET_ALIAS_TOKENS = {<TokenType.INET: 'INET'>, <TokenType.HLLSKETCH: 'HLLSKETCH'>, <TokenType.STAGE: 'STAGE'>, <TokenType.MEDIUMBLOB: 'MEDIUMBLOB'>, <TokenType.FIXEDSTRING: 'FIXEDSTRING'>, <TokenType.OBJECT: 'OBJECT'>, <TokenType.NCHAR: 'NCHAR'>, <TokenType.LONGBLOB: 'LONGBLOB'>, <TokenType.LIST: 'LIST'>, <TokenType.TRUE: 'TRUE'>, <TokenType.TIME: 'TIME'>, <TokenType.COMMENT: 'COMMENT'>, <TokenType.ALL: 'ALL'>, <TokenType.UINT256: 'UINT256'>, <TokenType.EXPORT: 'EXPORT'>, <TokenType.DETACH: 'DETACH'>, <TokenType.TIMESTAMP_S: 'TIMESTAMP_S'>, <TokenType.VOID: 'VOID'>, <TokenType.PARTITION: 'PARTITION'>, <TokenType.TINYBLOB: 'TINYBLOB'>, <TokenType.MEDIUMTEXT: 'MEDIUMTEXT'>, <TokenType.OVERLAPS: 'OVERLAPS'>, <TokenType.INTERVAL: 'INTERVAL'>, <TokenType.RENAME: 'RENAME'>, <TokenType.BOOLEAN: 'BOOLEAN'>, <TokenType.TEXT: 'TEXT'>, <TokenType.POLYGON: 'POLYGON'>, <TokenType.LOAD: 'LOAD'>, <TokenType.HSTORE: 'HSTORE'>, <TokenType.OBJECT_IDENTIFIER: 'OBJECT_IDENTIFIER'>, <TokenType.VOLATILE: 'VOLATILE'>, <TokenType.BIT: 'BIT'>, <TokenType.VIEW: 'VIEW'>, <TokenType.LOWCARDINALITY: 'LOWCARDINALITY'>, <TokenType.IPV6: 'IPV6'>, <TokenType.VARCHAR: 'VARCHAR'>, <TokenType.AGGREGATEFUNCTION: 'AGGREGATEFUNCTION'>, <TokenType.GET: 'GET'>, <TokenType.KEEP: 'KEEP'>, <TokenType.DATERANGE: 'DATERANGE'>, <TokenType.INT: 'INT'>, <TokenType.IS: 'IS'>, <TokenType.PIVOT: 'PIVOT'>, <TokenType.BIGSERIAL: 'BIGSERIAL'>, <TokenType.TINYTEXT: 'TINYTEXT'>, <TokenType.DATE: 'DATE'>, <TokenType.GEOMETRY: 'GEOMETRY'>, <TokenType.ANY: 'ANY'>, <TokenType.ENUM16: 'ENUM16'>, <TokenType.USMALLINT: 'USMALLINT'>, <TokenType.CURRENT_TIME: 'CURRENT_TIME'>, <TokenType.ENUM8: 'ENUM8'>, <TokenType.CURRENT_DATE: 'CURRENT_DATE'>, <TokenType.ARRAY: 'ARRAY'>, <TokenType.PERCENT: 'PERCENT'>, <TokenType.TSRANGE: 'TSRANGE'>, <TokenType.INT128: 'INT128'>, <TokenType.DECIMAL: 'DECIMAL'>, <TokenType.DECIMAL256: 'DECIMAL256'>, <TokenType.DICTIONARY: 'DICTIONARY'>, <TokenType.SERIAL: 'SERIAL'>, <TokenType.XML: 'XML'>, <TokenType.PROCEDURE: 'PROCEDURE'>, <TokenType.UNIQUE: 'UNIQUE'>, <TokenType.NVARCHAR: 'NVARCHAR'>, <TokenType.TIMETZ: 'TIMETZ'>, <TokenType.STORAGE_INTEGRATION: 'STORAGE_INTEGRATION'>, <TokenType.DATETIME: 'DATETIME'>, <TokenType.STRUCT: 'STRUCT'>, <TokenType.EXECUTE: 'EXECUTE'>, <TokenType.JSON: 'JSON'>, <TokenType.FILE_FORMAT: 'FILE_FORMAT'>, <TokenType.ISNULL: 'ISNULL'>, <TokenType.TIMESTAMPNTZ: 'TIMESTAMPNTZ'>, <TokenType.DYNAMIC: 'DYNAMIC'>, <TokenType.UINT: 'UINT'>, <TokenType.RECURSIVE: 'RECURSIVE'>, <TokenType.FINAL: 'FINAL'>, <TokenType.WAREHOUSE: 'WAREHOUSE'>, <TokenType.SINK: 'SINK'>, <TokenType.ESCAPE: 'ESCAPE'>, <TokenType.USE: 'USE'>, <TokenType.JSONB: 'JSONB'>, <TokenType.SMALLMONEY: 'SMALLMONEY'>, <TokenType.UMEDIUMINT: 'UMEDIUMINT'>, <TokenType.SMALLSERIAL: 'SMALLSERIAL'>, <TokenType.TDIGEST: 'TDIGEST'>, <TokenType.ROW: 'ROW'>, <TokenType.TAG: 'TAG'>, <TokenType.DATETIME2: 'DATETIME2'>, <TokenType.PRAGMA: 'PRAGMA'>, <TokenType.CHAR: 'CHAR'>, <TokenType.NEXT: 'NEXT'>, <TokenType.TIMESTAMPLTZ: 'TIMESTAMPLTZ'>, <TokenType.CURRENT_DATETIME: 'CURRENT_DATETIME'>, <TokenType.INT4RANGE: 'INT4RANGE'>, <TokenType.INT8RANGE: 'INT8RANGE'>, <TokenType.NUMRANGE: 'NUMRANGE'>, <TokenType.ASC: 'ASC'>, <TokenType.INT4MULTIRANGE: 'INT4MULTIRANGE'>, <TokenType.UDECIMAL: 'UDECIMAL'>, <TokenType.FUNCTION: 'FUNCTION'>, <TokenType.FALSE: 'FALSE'>, <TokenType.CURRENT_USER: 'CURRENT_USER'>, <TokenType.RANGE: 'RANGE'>, <TokenType.CURRENT_TIMESTAMP: 'CURRENT_TIMESTAMP'>, <TokenType.LIMIT: 'LIMIT'>, <TokenType.TIMESTAMP_NS: 'TIMESTAMP_NS'>, <TokenType.MONEY: 'MONEY'>, <TokenType.MULTILINESTRING: 'MULTILINESTRING'>, <TokenType.GEOGRAPHY: 'GEOGRAPHY'>, <TokenType.VAR: 'VAR'>, <TokenType.DECIMAL128: 'DECIMAL128'>, <TokenType.SOURCE: 'SOURCE'>, <TokenType.IPADDRESS: 'IPADDRESS'>, <TokenType.REPLACE: 'REPLACE'>, <TokenType.DATETIME64: 'DATETIME64'>, <TokenType.TSTZRANGE: 'TSTZRANGE'>, <TokenType.YEAR: 'YEAR'>, <TokenType.MERGE: 'MERGE'>, <TokenType.LINESTRING: 'LINESTRING'>, <TokenType.KILL: 'KILL'>, <TokenType.CUBE: 'CUBE'>, <TokenType.CURRENT_SCHEMA: 'CURRENT_SCHEMA'>, <TokenType.FORMAT: 'FORMAT'>, <TokenType.SUPER: 'SUPER'>, <TokenType.IMAGE: 'IMAGE'>, <TokenType.FLOAT: 'FLOAT'>, <TokenType.TRUNCATE: 'TRUNCATE'>, <TokenType.MULTIPOLYGON: 'MULTIPOLYGON'>, <TokenType.NULLABLE: 'NULLABLE'>, <TokenType.MEDIUMINT: 'MEDIUMINT'>, <TokenType.SOME: 'SOME'>, <TokenType.IPPREFIX: 'IPPREFIX'>, <TokenType.DIV: 'DIV'>, <TokenType.NAME: 'NAME'>, <TokenType.BINARY: 'BINARY'>, <TokenType.INT8MULTIRANGE: 'INT8MULTIRANGE'>, <TokenType.ROWS: 'ROWS'>, <TokenType.INDEX: 'INDEX'>, <TokenType.IDENTIFIER: 'IDENTIFIER'>, <TokenType.MAP: 'MAP'>, <TokenType.USERDEFINED: 'USERDEFINED'>, <TokenType.DELETE: 'DELETE'>, <TokenType.MODEL: 'MODEL'>, <TokenType.ROWVERSION: 'ROWVERSION'>, <TokenType.DECIMAL32: 'DECIMAL32'>, <TokenType.DATEMULTIRANGE: 'DATEMULTIRANGE'>, <TokenType.UNKNOWN: 'UNKNOWN'>, <TokenType.ENUM: 'ENUM'>, <TokenType.UDOUBLE: 'UDOUBLE'>, <TokenType.SETTINGS: 'SETTINGS'>, <TokenType.TIMESTAMPTZ: 'TIMESTAMPTZ'>, <TokenType.BLOB: 'BLOB'>, <TokenType.END: 'END'>, <TokenType.SMALLDATETIME: 'SMALLDATETIME'>, <TokenType.INT256: 'INT256'>, <TokenType.UNPIVOT: 'UNPIVOT'>, <TokenType.SMALLINT: 'SMALLINT'>, <TokenType.OPERATOR: 'OPERATOR'>, <TokenType.CONSTRAINT: 'CONSTRAINT'>, <TokenType.BIGDECIMAL: 'BIGDECIMAL'>, <TokenType.NULL: 'NULL'>, <TokenType.COMMIT: 'COMMIT'>, <TokenType.SCHEMA: 'SCHEMA'>, <TokenType.FIRST: 'FIRST'>, <TokenType.BPCHAR: 'BPCHAR'>, <TokenType.DOUBLE: 'DOUBLE'>, <TokenType.PUT: 'PUT'>, <TokenType.DESC: 'DESC'>, <TokenType.IPV4: 'IPV4'>, <TokenType.COPY: 'COPY'>, <TokenType.VARIANT: 'VARIANT'>, <TokenType.FILTER: 'FILTER'>, <TokenType.TIMESTAMP: 'TIMESTAMP'>, <TokenType.UTINYINT: 'UTINYINT'>, <TokenType.UUID: 'UUID'>, <TokenType.TOP: 'TOP'>, <TokenType.REFRESH: 'REFRESH'>, <TokenType.NUMMULTIRANGE: 'NUMMULTIRANGE'>, <TokenType.UINT128: 'UINT128'>, <TokenType.OVERWRITE: 'OVERWRITE'>, <TokenType.CASE: 'CASE'>, <TokenType.AUTO_INCREMENT: 'AUTO_INCREMENT'>, <TokenType.DEFAULT: 'DEFAULT'>, <TokenType.POINT: 'POINT'>, <TokenType.ORDINALITY: 'ORDINALITY'>, <TokenType.RING: 'RING'>, <TokenType.BEGIN: 'BEGIN'>, <TokenType.BIGINT: 'BIGINT'>, <TokenType.ROLLUP: 'ROLLUP'>, <TokenType.TIMESTAMP_MS: 'TIMESTAMP_MS'>, <TokenType.OFFSET: 'OFFSET'>, <TokenType.FOREIGN_KEY: 'FOREIGN_KEY'>, <TokenType.SEQUENCE: 'SEQUENCE'>, <TokenType.UBIGINT: 'UBIGINT'>, <TokenType.DATE32: 'DATE32'>, <TokenType.NESTED: 'NESTED'>, <TokenType.SHOW: 'SHOW'>, <TokenType.VARBINARY: 'VARBINARY'>, <TokenType.NOTHING: 'NOTHING'>, <TokenType.CACHE: 'CACHE'>, <TokenType.TEMPORARY: 'TEMPORARY'>, <TokenType.UPDATE: 'UPDATE'>, <TokenType.EXISTS: 'EXISTS'>, <TokenType.TSTZMULTIRANGE: 'TSTZMULTIRANGE'>, <TokenType.PSEUDO_TYPE: 'PSEUDO_TYPE'>, <TokenType.SIMPLEAGGREGATEFUNCTION: 'SIMPLEAGGREGATEFUNCTION'>, <TokenType.TINYINT: 'TINYINT'>, <TokenType.VECTOR: 'VECTOR'>, <TokenType.TABLE: 'TABLE'>, <TokenType.LONGTEXT: 'LONGTEXT'>, <TokenType.DATABASE: 'DATABASE'>, <TokenType.STREAMLIT: 'STREAMLIT'>, <TokenType.COMMAND: 'COMMAND'>, <TokenType.COLUMN: 'COLUMN'>, <TokenType.REFERENCES: 'REFERENCES'>, <TokenType.NAMESPACE: 'NAMESPACE'>, <TokenType.ATTACH: 'ATTACH'>, <TokenType.SET: 'SET'>, <TokenType.UNNEST: 'UNNEST'>, <TokenType.DECIMAL64: 'DECIMAL64'>, <TokenType.TSMULTIRANGE: 'TSMULTIRANGE'>, <TokenType.COLLATE: 'COLLATE'>, <TokenType.DESCRIBE: 'DESCRIBE'>}
SELECT_START_TOKENS = {<TokenType.WITH: 'WITH'>, <TokenType.SELECT: 'SELECT'>, <TokenType.L_PAREN: 'L_PAREN'>}
COPY_INTO_VARLEN_OPTIONS = {'COPY_OPTIONS', 'FILE_FORMAT', 'FORMAT_OPTIONS', 'CREDENTIAL'}
IS_JSON_PREDICATE_KIND = {'OBJECT', 'ARRAY', 'VALUE', 'SCALAR'}
ODBC_DATETIME_LITERALS = {'d': <class 'sqlglot.expressions.Date'>, 't': <class 'sqlglot.expressions.Time'>, 'ts': <class 'sqlglot.expressions.Timestamp'>}
ON_CONDITION_TOKENS = {'TRUE', 'NULL', 'FALSE', 'ERROR', 'EMPTY'}
PRIVILEGE_FOLLOW_TOKENS = {<TokenType.ON: 'ON'>, <TokenType.COMMA: 'COMMA'>, <TokenType.L_PAREN: 'L_PAREN'>}
DESCRIBE_STYLES = {'ANALYZE', 'HISTORY', 'FORMATTED', 'EXTENDED'}
ANALYZE_STYLES = {'BUFFER_USAGE_LIMIT', 'LOCAL', 'NO_WRITE_TO_BINLOG', 'FULL', 'SKIP_LOCKED', 'VERBOSE', 'SAMPLE'}
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
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
ALTER_RENAME_REQUIRES_COLUMN = True
JOINS_HAVE_EQUAL_PRECEDENCE = False
ZONE_AWARE_TIMESTAMP_CONSTRUCTOR = False
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):
1568    def reset(self):
1569        self.sql = ""
1570        self.errors = []
1571        self._tokens = []
1572        self._index = 0
1573        self._curr = None
1574        self._next = None
1575        self._prev = None
1576        self._prev_comments = None
1577        self._pipe_cte_counter = 0
def parse( self, raw_tokens: List[sqlglot.tokens.Token], sql: Optional[str] = None) -> List[Optional[sqlglot.expressions.Expression]]:
1579    def parse(
1580        self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
1581    ) -> t.List[t.Optional[exp.Expression]]:
1582        """
1583        Parses a list of tokens and returns a list of syntax trees, one tree
1584        per parsed SQL statement.
1585
1586        Args:
1587            raw_tokens: The list of tokens.
1588            sql: The original SQL string, used to produce helpful debug messages.
1589
1590        Returns:
1591            The list of the produced syntax trees.
1592        """
1593        return self._parse(
1594            parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
1595        )

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]]:
1597    def parse_into(
1598        self,
1599        expression_types: exp.IntoType,
1600        raw_tokens: t.List[Token],
1601        sql: t.Optional[str] = None,
1602    ) -> t.List[t.Optional[exp.Expression]]:
1603        """
1604        Parses a list of tokens into a given Expression type. If a collection of Expression
1605        types is given instead, this method will try to parse the token list into each one
1606        of them, stopping at the first for which the parsing succeeds.
1607
1608        Args:
1609            expression_types: The expression type(s) to try and parse the token list into.
1610            raw_tokens: The list of tokens.
1611            sql: The original SQL string, used to produce helpful debug messages.
1612
1613        Returns:
1614            The target Expression.
1615        """
1616        errors = []
1617        for expression_type in ensure_list(expression_types):
1618            parser = self.EXPRESSION_PARSERS.get(expression_type)
1619            if not parser:
1620                raise TypeError(f"No parser registered for {expression_type}")
1621
1622            try:
1623                return self._parse(parser, raw_tokens, sql)
1624            except ParseError as e:
1625                e.errors[0]["into_expression"] = expression_type
1626                errors.append(e)
1627
1628        raise ParseError(
1629            f"Failed to parse '{sql or raw_tokens}' into {expression_types}",
1630            errors=merge_errors(errors),
1631        ) 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:
1671    def check_errors(self) -> None:
1672        """Logs or raises any found errors, depending on the chosen error level setting."""
1673        if self.error_level == ErrorLevel.WARN:
1674            for error in self.errors:
1675                logger.error(str(error))
1676        elif self.error_level == ErrorLevel.RAISE and self.errors:
1677            raise ParseError(
1678                concat_messages(self.errors, self.max_errors),
1679                errors=merge_errors(self.errors),
1680            )

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:
1682    def raise_error(self, message: str, token: t.Optional[Token] = None) -> None:
1683        """
1684        Appends an error in the list of recorded errors or raises it, depending on the chosen
1685        error level setting.
1686        """
1687        token = token or self._curr or self._prev or Token.string("")
1688        start = token.start
1689        end = token.end + 1
1690        start_context = self.sql[max(start - self.error_message_context, 0) : start]
1691        highlight = self.sql[start:end]
1692        end_context = self.sql[end : end + self.error_message_context]
1693
1694        error = ParseError.new(
1695            f"{message}. Line {token.line}, Col: {token.col}.\n"
1696            f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
1697            description=message,
1698            line=token.line,
1699            col=token.col,
1700            start_context=start_context,
1701            highlight=highlight,
1702            end_context=end_context,
1703        )
1704
1705        if self.error_level == ErrorLevel.IMMEDIATE:
1706            raise error
1707
1708        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:
1710    def expression(
1711        self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs
1712    ) -> E:
1713        """
1714        Creates a new, validated Expression.
1715
1716        Args:
1717            exp_class: The expression class to instantiate.
1718            comments: An optional list of comments to attach to the expression.
1719            kwargs: The arguments to set for the expression along with their respective values.
1720
1721        Returns:
1722            The target expression.
1723        """
1724        instance = exp_class(**kwargs)
1725        instance.add_comments(comments) if comments else self._add_comments(instance)
1726        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:
1733    def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E:
1734        """
1735        Validates an Expression, making sure that all its mandatory arguments are set.
1736
1737        Args:
1738            expression: The expression to validate.
1739            args: An optional list of items that was used to instantiate the expression, if it's a Func.
1740
1741        Returns:
1742            The validated expression.
1743        """
1744        if self.error_level != ErrorLevel.IGNORE:
1745            for error_message in expression.error_messages(args):
1746                self.raise_error(error_message)
1747
1748        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], consume_pipe: bool = False) -> Optional[sqlglot.expressions.Expression]:
4763    def parse_set_operation(
4764        self, this: t.Optional[exp.Expression], consume_pipe: bool = False
4765    ) -> t.Optional[exp.Expression]:
4766        start = self._index
4767        _, side_token, kind_token = self._parse_join_parts()
4768
4769        side = side_token.text if side_token else None
4770        kind = kind_token.text if kind_token else None
4771
4772        if not self._match_set(self.SET_OPERATIONS):
4773            self._retreat(start)
4774            return None
4775
4776        token_type = self._prev.token_type
4777
4778        if token_type == TokenType.UNION:
4779            operation: t.Type[exp.SetOperation] = exp.Union
4780        elif token_type == TokenType.EXCEPT:
4781            operation = exp.Except
4782        else:
4783            operation = exp.Intersect
4784
4785        comments = self._prev.comments
4786
4787        if self._match(TokenType.DISTINCT):
4788            distinct: t.Optional[bool] = True
4789        elif self._match(TokenType.ALL):
4790            distinct = False
4791        else:
4792            distinct = self.dialect.SET_OP_DISTINCT_BY_DEFAULT[operation]
4793            if distinct is None:
4794                self.raise_error(f"Expected DISTINCT or ALL for {operation.__name__}")
4795
4796        by_name = self._match_text_seq("BY", "NAME") or self._match_text_seq(
4797            "STRICT", "CORRESPONDING"
4798        )
4799        if self._match_text_seq("CORRESPONDING"):
4800            by_name = True
4801            if not side and not kind:
4802                kind = "INNER"
4803
4804        on_column_list = None
4805        if by_name and self._match_texts(("ON", "BY")):
4806            on_column_list = self._parse_wrapped_csv(self._parse_column)
4807
4808        expression = self._parse_select(
4809            nested=True, parse_set_operation=False, consume_pipe=consume_pipe
4810        )
4811
4812        return self.expression(
4813            operation,
4814            comments=comments,
4815            this=this,
4816            distinct=distinct,
4817            by_name=by_name,
4818            expression=expression,
4819            side=side,
4820            kind=kind,
4821            on=on_column_list,
4822        )
errors
sql