sqlglot.parser
1from __future__ import annotations 2 3import logging 4import typing as t 5from collections import defaultdict 6 7from sqlglot import exp 8from sqlglot.errors import ErrorLevel, ParseError, concat_messages, merge_errors 9from sqlglot.helper import apply_index_offset, ensure_list, seq_get 10from sqlglot.time import format_time 11from sqlglot.tokens import Token, Tokenizer, TokenType 12from sqlglot.trie import TrieResult, in_trie, new_trie 13 14if t.TYPE_CHECKING: 15 from sqlglot._typing import E 16 17logger = logging.getLogger("sqlglot") 18 19 20def parse_var_map(args: t.List) -> exp.StarMap | exp.VarMap: 21 if len(args) == 1 and args[0].is_star: 22 return exp.StarMap(this=args[0]) 23 24 keys = [] 25 values = [] 26 for i in range(0, len(args), 2): 27 keys.append(args[i]) 28 values.append(args[i + 1]) 29 30 return exp.VarMap( 31 keys=exp.Array(expressions=keys), 32 values=exp.Array(expressions=values), 33 ) 34 35 36def parse_like(args: t.List) -> exp.Escape | exp.Like: 37 like = exp.Like(this=seq_get(args, 1), expression=seq_get(args, 0)) 38 return exp.Escape(this=like, expression=seq_get(args, 2)) if len(args) > 2 else like 39 40 41def binary_range_parser( 42 expr_type: t.Type[exp.Expression], 43) -> t.Callable[[Parser, t.Optional[exp.Expression]], t.Optional[exp.Expression]]: 44 return lambda self, this: self._parse_escape( 45 self.expression(expr_type, this=this, expression=self._parse_bitwise()) 46 ) 47 48 49class _Parser(type): 50 def __new__(cls, clsname, bases, attrs): 51 klass = super().__new__(cls, clsname, bases, attrs) 52 53 klass.SHOW_TRIE = new_trie(key.split(" ") for key in klass.SHOW_PARSERS) 54 klass.SET_TRIE = new_trie(key.split(" ") for key in klass.SET_PARSERS) 55 56 return klass 57 58 59class Parser(metaclass=_Parser): 60 """ 61 Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree. 62 63 Args: 64 error_level: The desired error level. 65 Default: ErrorLevel.IMMEDIATE 66 error_message_context: Determines the amount of context to capture from a 67 query string when displaying the error message (in number of characters). 68 Default: 100 69 max_errors: Maximum number of error messages to include in a raised ParseError. 70 This is only relevant if error_level is ErrorLevel.RAISE. 71 Default: 3 72 """ 73 74 FUNCTIONS: t.Dict[str, t.Callable] = { 75 **{name: f.from_arg_list for f in exp.ALL_FUNCTIONS for name in f.sql_names()}, 76 "DATE_TO_DATE_STR": lambda args: exp.Cast( 77 this=seq_get(args, 0), 78 to=exp.DataType(this=exp.DataType.Type.TEXT), 79 ), 80 "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)), 81 "LIKE": parse_like, 82 "TIME_TO_TIME_STR": lambda args: exp.Cast( 83 this=seq_get(args, 0), 84 to=exp.DataType(this=exp.DataType.Type.TEXT), 85 ), 86 "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring( 87 this=exp.Cast( 88 this=seq_get(args, 0), 89 to=exp.DataType(this=exp.DataType.Type.TEXT), 90 ), 91 start=exp.Literal.number(1), 92 length=exp.Literal.number(10), 93 ), 94 "VAR_MAP": parse_var_map, 95 } 96 97 NO_PAREN_FUNCTIONS = { 98 TokenType.CURRENT_DATE: exp.CurrentDate, 99 TokenType.CURRENT_DATETIME: exp.CurrentDate, 100 TokenType.CURRENT_TIME: exp.CurrentTime, 101 TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp, 102 TokenType.CURRENT_USER: exp.CurrentUser, 103 } 104 105 STRUCT_TYPE_TOKENS = { 106 TokenType.NESTED, 107 TokenType.STRUCT, 108 } 109 110 NESTED_TYPE_TOKENS = { 111 TokenType.ARRAY, 112 TokenType.LOWCARDINALITY, 113 TokenType.MAP, 114 TokenType.NULLABLE, 115 *STRUCT_TYPE_TOKENS, 116 } 117 118 ENUM_TYPE_TOKENS = { 119 TokenType.ENUM, 120 TokenType.ENUM8, 121 TokenType.ENUM16, 122 } 123 124 TYPE_TOKENS = { 125 TokenType.BIT, 126 TokenType.BOOLEAN, 127 TokenType.TINYINT, 128 TokenType.UTINYINT, 129 TokenType.SMALLINT, 130 TokenType.USMALLINT, 131 TokenType.INT, 132 TokenType.UINT, 133 TokenType.BIGINT, 134 TokenType.UBIGINT, 135 TokenType.INT128, 136 TokenType.UINT128, 137 TokenType.INT256, 138 TokenType.UINT256, 139 TokenType.MEDIUMINT, 140 TokenType.FIXEDSTRING, 141 TokenType.FLOAT, 142 TokenType.DOUBLE, 143 TokenType.CHAR, 144 TokenType.NCHAR, 145 TokenType.VARCHAR, 146 TokenType.NVARCHAR, 147 TokenType.TEXT, 148 TokenType.MEDIUMTEXT, 149 TokenType.LONGTEXT, 150 TokenType.MEDIUMBLOB, 151 TokenType.LONGBLOB, 152 TokenType.BINARY, 153 TokenType.VARBINARY, 154 TokenType.JSON, 155 TokenType.JSONB, 156 TokenType.INTERVAL, 157 TokenType.TIME, 158 TokenType.TIMETZ, 159 TokenType.TIMESTAMP, 160 TokenType.TIMESTAMPTZ, 161 TokenType.TIMESTAMPLTZ, 162 TokenType.DATETIME, 163 TokenType.DATETIME64, 164 TokenType.DATE, 165 TokenType.INT4RANGE, 166 TokenType.INT4MULTIRANGE, 167 TokenType.INT8RANGE, 168 TokenType.INT8MULTIRANGE, 169 TokenType.NUMRANGE, 170 TokenType.NUMMULTIRANGE, 171 TokenType.TSRANGE, 172 TokenType.TSMULTIRANGE, 173 TokenType.TSTZRANGE, 174 TokenType.TSTZMULTIRANGE, 175 TokenType.DATERANGE, 176 TokenType.DATEMULTIRANGE, 177 TokenType.DECIMAL, 178 TokenType.BIGDECIMAL, 179 TokenType.UUID, 180 TokenType.GEOGRAPHY, 181 TokenType.GEOMETRY, 182 TokenType.HLLSKETCH, 183 TokenType.HSTORE, 184 TokenType.PSEUDO_TYPE, 185 TokenType.SUPER, 186 TokenType.SERIAL, 187 TokenType.SMALLSERIAL, 188 TokenType.BIGSERIAL, 189 TokenType.XML, 190 TokenType.YEAR, 191 TokenType.UNIQUEIDENTIFIER, 192 TokenType.USERDEFINED, 193 TokenType.MONEY, 194 TokenType.SMALLMONEY, 195 TokenType.ROWVERSION, 196 TokenType.IMAGE, 197 TokenType.VARIANT, 198 TokenType.OBJECT, 199 TokenType.INET, 200 TokenType.IPADDRESS, 201 TokenType.IPPREFIX, 202 TokenType.UNKNOWN, 203 TokenType.NULL, 204 *ENUM_TYPE_TOKENS, 205 *NESTED_TYPE_TOKENS, 206 } 207 208 SUBQUERY_PREDICATES = { 209 TokenType.ANY: exp.Any, 210 TokenType.ALL: exp.All, 211 TokenType.EXISTS: exp.Exists, 212 TokenType.SOME: exp.Any, 213 } 214 215 RESERVED_KEYWORDS = { 216 *Tokenizer.SINGLE_TOKENS.values(), 217 TokenType.SELECT, 218 } 219 220 DB_CREATABLES = { 221 TokenType.DATABASE, 222 TokenType.SCHEMA, 223 TokenType.TABLE, 224 TokenType.VIEW, 225 TokenType.DICTIONARY, 226 } 227 228 CREATABLES = { 229 TokenType.COLUMN, 230 TokenType.FUNCTION, 231 TokenType.INDEX, 232 TokenType.PROCEDURE, 233 *DB_CREATABLES, 234 } 235 236 # Tokens that can represent identifiers 237 ID_VAR_TOKENS = { 238 TokenType.VAR, 239 TokenType.ANTI, 240 TokenType.APPLY, 241 TokenType.ASC, 242 TokenType.AUTO_INCREMENT, 243 TokenType.BEGIN, 244 TokenType.CACHE, 245 TokenType.CASE, 246 TokenType.COLLATE, 247 TokenType.COMMAND, 248 TokenType.COMMENT, 249 TokenType.COMMIT, 250 TokenType.CONSTRAINT, 251 TokenType.DEFAULT, 252 TokenType.DELETE, 253 TokenType.DESC, 254 TokenType.DESCRIBE, 255 TokenType.DICTIONARY, 256 TokenType.DIV, 257 TokenType.END, 258 TokenType.EXECUTE, 259 TokenType.ESCAPE, 260 TokenType.FALSE, 261 TokenType.FIRST, 262 TokenType.FILTER, 263 TokenType.FORMAT, 264 TokenType.FULL, 265 TokenType.IS, 266 TokenType.ISNULL, 267 TokenType.INTERVAL, 268 TokenType.KEEP, 269 TokenType.LEFT, 270 TokenType.LOAD, 271 TokenType.MERGE, 272 TokenType.NATURAL, 273 TokenType.NEXT, 274 TokenType.OFFSET, 275 TokenType.ORDINALITY, 276 TokenType.OVERWRITE, 277 TokenType.PARTITION, 278 TokenType.PERCENT, 279 TokenType.PIVOT, 280 TokenType.PRAGMA, 281 TokenType.RANGE, 282 TokenType.REFERENCES, 283 TokenType.RIGHT, 284 TokenType.ROW, 285 TokenType.ROWS, 286 TokenType.SEMI, 287 TokenType.SET, 288 TokenType.SETTINGS, 289 TokenType.SHOW, 290 TokenType.TEMPORARY, 291 TokenType.TOP, 292 TokenType.TRUE, 293 TokenType.UNIQUE, 294 TokenType.UNPIVOT, 295 TokenType.UPDATE, 296 TokenType.VOLATILE, 297 TokenType.WINDOW, 298 *CREATABLES, 299 *SUBQUERY_PREDICATES, 300 *TYPE_TOKENS, 301 *NO_PAREN_FUNCTIONS, 302 } 303 304 INTERVAL_VARS = ID_VAR_TOKENS - {TokenType.END} 305 306 TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - { 307 TokenType.APPLY, 308 TokenType.ASOF, 309 TokenType.FULL, 310 TokenType.LEFT, 311 TokenType.LOCK, 312 TokenType.NATURAL, 313 TokenType.OFFSET, 314 TokenType.RIGHT, 315 TokenType.WINDOW, 316 } 317 318 COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS} 319 320 UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET} 321 322 TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"} 323 324 FUNC_TOKENS = { 325 TokenType.COMMAND, 326 TokenType.CURRENT_DATE, 327 TokenType.CURRENT_DATETIME, 328 TokenType.CURRENT_TIMESTAMP, 329 TokenType.CURRENT_TIME, 330 TokenType.CURRENT_USER, 331 TokenType.FILTER, 332 TokenType.FIRST, 333 TokenType.FORMAT, 334 TokenType.GLOB, 335 TokenType.IDENTIFIER, 336 TokenType.INDEX, 337 TokenType.ISNULL, 338 TokenType.ILIKE, 339 TokenType.INSERT, 340 TokenType.LIKE, 341 TokenType.MERGE, 342 TokenType.OFFSET, 343 TokenType.PRIMARY_KEY, 344 TokenType.RANGE, 345 TokenType.REPLACE, 346 TokenType.RLIKE, 347 TokenType.ROW, 348 TokenType.UNNEST, 349 TokenType.VAR, 350 TokenType.LEFT, 351 TokenType.RIGHT, 352 TokenType.DATE, 353 TokenType.DATETIME, 354 TokenType.TABLE, 355 TokenType.TIMESTAMP, 356 TokenType.TIMESTAMPTZ, 357 TokenType.WINDOW, 358 TokenType.XOR, 359 *TYPE_TOKENS, 360 *SUBQUERY_PREDICATES, 361 } 362 363 CONJUNCTION = { 364 TokenType.AND: exp.And, 365 TokenType.OR: exp.Or, 366 } 367 368 EQUALITY = { 369 TokenType.EQ: exp.EQ, 370 TokenType.NEQ: exp.NEQ, 371 TokenType.NULLSAFE_EQ: exp.NullSafeEQ, 372 } 373 374 COMPARISON = { 375 TokenType.GT: exp.GT, 376 TokenType.GTE: exp.GTE, 377 TokenType.LT: exp.LT, 378 TokenType.LTE: exp.LTE, 379 } 380 381 BITWISE = { 382 TokenType.AMP: exp.BitwiseAnd, 383 TokenType.CARET: exp.BitwiseXor, 384 TokenType.PIPE: exp.BitwiseOr, 385 TokenType.DPIPE: exp.DPipe, 386 } 387 388 TERM = { 389 TokenType.DASH: exp.Sub, 390 TokenType.PLUS: exp.Add, 391 TokenType.MOD: exp.Mod, 392 TokenType.COLLATE: exp.Collate, 393 } 394 395 FACTOR = { 396 TokenType.DIV: exp.IntDiv, 397 TokenType.LR_ARROW: exp.Distance, 398 TokenType.SLASH: exp.Div, 399 TokenType.STAR: exp.Mul, 400 } 401 402 TIMES = { 403 TokenType.TIME, 404 TokenType.TIMETZ, 405 } 406 407 TIMESTAMPS = { 408 TokenType.TIMESTAMP, 409 TokenType.TIMESTAMPTZ, 410 TokenType.TIMESTAMPLTZ, 411 *TIMES, 412 } 413 414 SET_OPERATIONS = { 415 TokenType.UNION, 416 TokenType.INTERSECT, 417 TokenType.EXCEPT, 418 } 419 420 JOIN_METHODS = { 421 TokenType.NATURAL, 422 TokenType.ASOF, 423 } 424 425 JOIN_SIDES = { 426 TokenType.LEFT, 427 TokenType.RIGHT, 428 TokenType.FULL, 429 } 430 431 JOIN_KINDS = { 432 TokenType.INNER, 433 TokenType.OUTER, 434 TokenType.CROSS, 435 TokenType.SEMI, 436 TokenType.ANTI, 437 } 438 439 JOIN_HINTS: t.Set[str] = set() 440 441 LAMBDAS = { 442 TokenType.ARROW: lambda self, expressions: self.expression( 443 exp.Lambda, 444 this=self._replace_lambda( 445 self._parse_conjunction(), 446 {node.name for node in expressions}, 447 ), 448 expressions=expressions, 449 ), 450 TokenType.FARROW: lambda self, expressions: self.expression( 451 exp.Kwarg, 452 this=exp.var(expressions[0].name), 453 expression=self._parse_conjunction(), 454 ), 455 } 456 457 COLUMN_OPERATORS = { 458 TokenType.DOT: None, 459 TokenType.DCOLON: lambda self, this, to: self.expression( 460 exp.Cast if self.STRICT_CAST else exp.TryCast, 461 this=this, 462 to=to, 463 ), 464 TokenType.ARROW: lambda self, this, path: self.expression( 465 exp.JSONExtract, 466 this=this, 467 expression=path, 468 ), 469 TokenType.DARROW: lambda self, this, path: self.expression( 470 exp.JSONExtractScalar, 471 this=this, 472 expression=path, 473 ), 474 TokenType.HASH_ARROW: lambda self, this, path: self.expression( 475 exp.JSONBExtract, 476 this=this, 477 expression=path, 478 ), 479 TokenType.DHASH_ARROW: lambda self, this, path: self.expression( 480 exp.JSONBExtractScalar, 481 this=this, 482 expression=path, 483 ), 484 TokenType.PLACEHOLDER: lambda self, this, key: self.expression( 485 exp.JSONBContains, 486 this=this, 487 expression=key, 488 ), 489 } 490 491 EXPRESSION_PARSERS = { 492 exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY), 493 exp.Column: lambda self: self._parse_column(), 494 exp.Condition: lambda self: self._parse_conjunction(), 495 exp.DataType: lambda self: self._parse_types(allow_identifiers=False), 496 exp.Expression: lambda self: self._parse_statement(), 497 exp.From: lambda self: self._parse_from(), 498 exp.Group: lambda self: self._parse_group(), 499 exp.Having: lambda self: self._parse_having(), 500 exp.Identifier: lambda self: self._parse_id_var(), 501 exp.Join: lambda self: self._parse_join(), 502 exp.Lambda: lambda self: self._parse_lambda(), 503 exp.Lateral: lambda self: self._parse_lateral(), 504 exp.Limit: lambda self: self._parse_limit(), 505 exp.Offset: lambda self: self._parse_offset(), 506 exp.Order: lambda self: self._parse_order(), 507 exp.Ordered: lambda self: self._parse_ordered(), 508 exp.Properties: lambda self: self._parse_properties(), 509 exp.Qualify: lambda self: self._parse_qualify(), 510 exp.Returning: lambda self: self._parse_returning(), 511 exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY), 512 exp.Table: lambda self: self._parse_table_parts(), 513 exp.TableAlias: lambda self: self._parse_table_alias(), 514 exp.Where: lambda self: self._parse_where(), 515 exp.Window: lambda self: self._parse_named_window(), 516 exp.With: lambda self: self._parse_with(), 517 "JOIN_TYPE": lambda self: self._parse_join_parts(), 518 } 519 520 STATEMENT_PARSERS = { 521 TokenType.ALTER: lambda self: self._parse_alter(), 522 TokenType.BEGIN: lambda self: self._parse_transaction(), 523 TokenType.CACHE: lambda self: self._parse_cache(), 524 TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(), 525 TokenType.COMMENT: lambda self: self._parse_comment(), 526 TokenType.CREATE: lambda self: self._parse_create(), 527 TokenType.DELETE: lambda self: self._parse_delete(), 528 TokenType.DESC: lambda self: self._parse_describe(), 529 TokenType.DESCRIBE: lambda self: self._parse_describe(), 530 TokenType.DROP: lambda self: self._parse_drop(), 531 TokenType.INSERT: lambda self: self._parse_insert(), 532 TokenType.LOAD: lambda self: self._parse_load(), 533 TokenType.MERGE: lambda self: self._parse_merge(), 534 TokenType.PIVOT: lambda self: self._parse_simplified_pivot(), 535 TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()), 536 TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(), 537 TokenType.SET: lambda self: self._parse_set(), 538 TokenType.UNCACHE: lambda self: self._parse_uncache(), 539 TokenType.UPDATE: lambda self: self._parse_update(), 540 TokenType.USE: lambda self: self.expression( 541 exp.Use, 542 kind=self._match_texts(("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA")) 543 and exp.var(self._prev.text), 544 this=self._parse_table(schema=False), 545 ), 546 } 547 548 UNARY_PARSERS = { 549 TokenType.PLUS: lambda self: self._parse_unary(), # Unary + is handled as a no-op 550 TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()), 551 TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()), 552 TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()), 553 } 554 555 PRIMARY_PARSERS = { 556 TokenType.STRING: lambda self, token: self.expression( 557 exp.Literal, this=token.text, is_string=True 558 ), 559 TokenType.NUMBER: lambda self, token: self.expression( 560 exp.Literal, this=token.text, is_string=False 561 ), 562 TokenType.STAR: lambda self, _: self.expression( 563 exp.Star, **{"except": self._parse_except(), "replace": self._parse_replace()} 564 ), 565 TokenType.NULL: lambda self, _: self.expression(exp.Null), 566 TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True), 567 TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False), 568 TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text), 569 TokenType.HEX_STRING: lambda self, token: self.expression(exp.HexString, this=token.text), 570 TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text), 571 TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token), 572 TokenType.NATIONAL_STRING: lambda self, token: self.expression( 573 exp.National, this=token.text 574 ), 575 TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text), 576 TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(), 577 } 578 579 PLACEHOLDER_PARSERS = { 580 TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder), 581 TokenType.PARAMETER: lambda self: self._parse_parameter(), 582 TokenType.COLON: lambda self: self.expression(exp.Placeholder, this=self._prev.text) 583 if self._match(TokenType.NUMBER) or self._match_set(self.ID_VAR_TOKENS) 584 else None, 585 } 586 587 RANGE_PARSERS = { 588 TokenType.BETWEEN: lambda self, this: self._parse_between(this), 589 TokenType.GLOB: binary_range_parser(exp.Glob), 590 TokenType.ILIKE: binary_range_parser(exp.ILike), 591 TokenType.IN: lambda self, this: self._parse_in(this), 592 TokenType.IRLIKE: binary_range_parser(exp.RegexpILike), 593 TokenType.IS: lambda self, this: self._parse_is(this), 594 TokenType.LIKE: binary_range_parser(exp.Like), 595 TokenType.OVERLAPS: binary_range_parser(exp.Overlaps), 596 TokenType.RLIKE: binary_range_parser(exp.RegexpLike), 597 TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo), 598 TokenType.FOR: lambda self, this: self._parse_comprehension(this), 599 } 600 601 PROPERTY_PARSERS: t.Dict[str, t.Callable] = { 602 "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty), 603 "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty), 604 "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(), 605 "CHARACTER SET": lambda self: self._parse_character_set(), 606 "CHECKSUM": lambda self: self._parse_checksum(), 607 "CLUSTER BY": lambda self: self._parse_cluster(), 608 "CLUSTERED": lambda self: self._parse_clustered_by(), 609 "COLLATE": lambda self: self._parse_property_assignment(exp.CollateProperty), 610 "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty), 611 "COPY": lambda self: self._parse_copy_property(), 612 "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs), 613 "DEFINER": lambda self: self._parse_definer(), 614 "DETERMINISTIC": lambda self: self.expression( 615 exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE") 616 ), 617 "DISTKEY": lambda self: self._parse_distkey(), 618 "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty), 619 "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty), 620 "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty), 621 "EXTERNAL": lambda self: self.expression(exp.ExternalProperty), 622 "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs), 623 "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty), 624 "FREESPACE": lambda self: self._parse_freespace(), 625 "HEAP": lambda self: self.expression(exp.HeapProperty), 626 "IMMUTABLE": lambda self: self.expression( 627 exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE") 628 ), 629 "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs), 630 "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty), 631 "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"), 632 "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"), 633 "LIKE": lambda self: self._parse_create_like(), 634 "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty), 635 "LOCK": lambda self: self._parse_locking(), 636 "LOCKING": lambda self: self._parse_locking(), 637 "LOG": lambda self, **kwargs: self._parse_log(**kwargs), 638 "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty), 639 "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs), 640 "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True), 641 "NO": lambda self: self._parse_no_property(), 642 "ON": lambda self: self._parse_on_property(), 643 "ORDER BY": lambda self: self._parse_order(skip_order_token=True), 644 "PARTITION BY": lambda self: self._parse_partitioned_by(), 645 "PARTITIONED BY": lambda self: self._parse_partitioned_by(), 646 "PARTITIONED_BY": lambda self: self._parse_partitioned_by(), 647 "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True), 648 "RANGE": lambda self: self._parse_dict_range(this="RANGE"), 649 "RETURNS": lambda self: self._parse_returns(), 650 "ROW": lambda self: self._parse_row(), 651 "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty), 652 "SET": lambda self: self.expression(exp.SetProperty, multi=False), 653 "SETTINGS": lambda self: self.expression( 654 exp.SettingsProperty, expressions=self._parse_csv(self._parse_set_item) 655 ), 656 "SORTKEY": lambda self: self._parse_sortkey(), 657 "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"), 658 "STABLE": lambda self: self.expression( 659 exp.StabilityProperty, this=exp.Literal.string("STABLE") 660 ), 661 "STORED": lambda self: self._parse_stored(), 662 "TBLPROPERTIES": lambda self: self._parse_wrapped_csv(self._parse_property), 663 "TEMP": lambda self: self.expression(exp.TemporaryProperty), 664 "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty), 665 "TO": lambda self: self._parse_to_table(), 666 "TRANSIENT": lambda self: self.expression(exp.TransientProperty), 667 "TTL": lambda self: self._parse_ttl(), 668 "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty), 669 "VOLATILE": lambda self: self._parse_volatile_property(), 670 "WITH": lambda self: self._parse_with_property(), 671 } 672 673 CONSTRAINT_PARSERS = { 674 "AUTOINCREMENT": lambda self: self._parse_auto_increment(), 675 "AUTO_INCREMENT": lambda self: self._parse_auto_increment(), 676 "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False), 677 "CHARACTER SET": lambda self: self.expression( 678 exp.CharacterSetColumnConstraint, this=self._parse_var_or_string() 679 ), 680 "CHECK": lambda self: self.expression( 681 exp.CheckColumnConstraint, this=self._parse_wrapped(self._parse_conjunction) 682 ), 683 "COLLATE": lambda self: self.expression( 684 exp.CollateColumnConstraint, this=self._parse_var() 685 ), 686 "COMMENT": lambda self: self.expression( 687 exp.CommentColumnConstraint, this=self._parse_string() 688 ), 689 "COMPRESS": lambda self: self._parse_compress(), 690 "CLUSTERED": lambda self: self.expression( 691 exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered) 692 ), 693 "NONCLUSTERED": lambda self: self.expression( 694 exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered) 695 ), 696 "DEFAULT": lambda self: self.expression( 697 exp.DefaultColumnConstraint, this=self._parse_bitwise() 698 ), 699 "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()), 700 "FOREIGN KEY": lambda self: self._parse_foreign_key(), 701 "FORMAT": lambda self: self.expression( 702 exp.DateFormatColumnConstraint, this=self._parse_var_or_string() 703 ), 704 "GENERATED": lambda self: self._parse_generated_as_identity(), 705 "IDENTITY": lambda self: self._parse_auto_increment(), 706 "INLINE": lambda self: self._parse_inline(), 707 "LIKE": lambda self: self._parse_create_like(), 708 "NOT": lambda self: self._parse_not_constraint(), 709 "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True), 710 "ON": lambda self: ( 711 self._match(TokenType.UPDATE) 712 and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function()) 713 ) 714 or self.expression(exp.OnProperty, this=self._parse_id_var()), 715 "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()), 716 "PRIMARY KEY": lambda self: self._parse_primary_key(), 717 "REFERENCES": lambda self: self._parse_references(match=False), 718 "TITLE": lambda self: self.expression( 719 exp.TitleColumnConstraint, this=self._parse_var_or_string() 720 ), 721 "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]), 722 "UNIQUE": lambda self: self._parse_unique(), 723 "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint), 724 "WITH": lambda self: self.expression( 725 exp.Properties, expressions=self._parse_wrapped_csv(self._parse_property) 726 ), 727 } 728 729 ALTER_PARSERS = { 730 "ADD": lambda self: self._parse_alter_table_add(), 731 "ALTER": lambda self: self._parse_alter_table_alter(), 732 "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()), 733 "DROP": lambda self: self._parse_alter_table_drop(), 734 "RENAME": lambda self: self._parse_alter_table_rename(), 735 } 736 737 SCHEMA_UNNAMED_CONSTRAINTS = {"CHECK", "FOREIGN KEY", "LIKE", "PRIMARY KEY", "UNIQUE"} 738 739 NO_PAREN_FUNCTION_PARSERS = { 740 "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()), 741 "CASE": lambda self: self._parse_case(), 742 "IF": lambda self: self._parse_if(), 743 "NEXT": lambda self: self._parse_next_value_for(), 744 } 745 746 INVALID_FUNC_NAME_TOKENS = { 747 TokenType.IDENTIFIER, 748 TokenType.STRING, 749 } 750 751 FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"} 752 753 FUNCTION_PARSERS = { 754 "ANY_VALUE": lambda self: self._parse_any_value(), 755 "CAST": lambda self: self._parse_cast(self.STRICT_CAST), 756 "CONCAT": lambda self: self._parse_concat(), 757 "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST), 758 "DECODE": lambda self: self._parse_decode(), 759 "EXTRACT": lambda self: self._parse_extract(), 760 "JSON_OBJECT": lambda self: self._parse_json_object(), 761 "LOG": lambda self: self._parse_logarithm(), 762 "MATCH": lambda self: self._parse_match_against(), 763 "OPENJSON": lambda self: self._parse_open_json(), 764 "POSITION": lambda self: self._parse_position(), 765 "SAFE_CAST": lambda self: self._parse_cast(False), 766 "STRING_AGG": lambda self: self._parse_string_agg(), 767 "SUBSTRING": lambda self: self._parse_substring(), 768 "TRIM": lambda self: self._parse_trim(), 769 "TRY_CAST": lambda self: self._parse_cast(False), 770 "TRY_CONVERT": lambda self: self._parse_convert(False), 771 } 772 773 QUERY_MODIFIER_PARSERS = { 774 TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()), 775 TokenType.WHERE: lambda self: ("where", self._parse_where()), 776 TokenType.GROUP_BY: lambda self: ("group", self._parse_group()), 777 TokenType.HAVING: lambda self: ("having", self._parse_having()), 778 TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()), 779 TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()), 780 TokenType.ORDER_BY: lambda self: ("order", self._parse_order()), 781 TokenType.LIMIT: lambda self: ("limit", self._parse_limit()), 782 TokenType.FETCH: lambda self: ("limit", self._parse_limit()), 783 TokenType.OFFSET: lambda self: ("offset", self._parse_offset()), 784 TokenType.FOR: lambda self: ("locks", self._parse_locks()), 785 TokenType.LOCK: lambda self: ("locks", self._parse_locks()), 786 TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)), 787 TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)), 788 TokenType.CLUSTER_BY: lambda self: ( 789 "cluster", 790 self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY), 791 ), 792 TokenType.DISTRIBUTE_BY: lambda self: ( 793 "distribute", 794 self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY), 795 ), 796 TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)), 797 TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)), 798 TokenType.START_WITH: lambda self: ("connect", self._parse_connect()), 799 } 800 801 SET_PARSERS = { 802 "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"), 803 "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"), 804 "SESSION": lambda self: self._parse_set_item_assignment("SESSION"), 805 "TRANSACTION": lambda self: self._parse_set_transaction(), 806 } 807 808 SHOW_PARSERS: t.Dict[str, t.Callable] = {} 809 810 TYPE_LITERAL_PARSERS: t.Dict[exp.DataType.Type, t.Callable] = {} 811 812 MODIFIABLES = (exp.Subquery, exp.Subqueryable, exp.Table) 813 814 DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN} 815 816 PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE} 817 818 TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"} 819 TRANSACTION_CHARACTERISTICS = { 820 "ISOLATION LEVEL REPEATABLE READ", 821 "ISOLATION LEVEL READ COMMITTED", 822 "ISOLATION LEVEL READ UNCOMMITTED", 823 "ISOLATION LEVEL SERIALIZABLE", 824 "READ WRITE", 825 "READ ONLY", 826 } 827 828 INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"} 829 830 CLONE_KINDS = {"TIMESTAMP", "OFFSET", "STATEMENT"} 831 832 TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE} 833 834 WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS} 835 WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER} 836 WINDOW_SIDES = {"FOLLOWING", "PRECEDING"} 837 838 ADD_CONSTRAINT_TOKENS = {TokenType.CONSTRAINT, TokenType.PRIMARY_KEY, TokenType.FOREIGN_KEY} 839 840 DISTINCT_TOKENS = {TokenType.DISTINCT} 841 842 STRICT_CAST = True 843 844 # A NULL arg in CONCAT yields NULL by default 845 CONCAT_NULL_OUTPUTS_STRING = False 846 847 PREFIXED_PIVOT_COLUMNS = False 848 IDENTIFY_PIVOT_STRINGS = False 849 850 LOG_BASE_FIRST = True 851 LOG_DEFAULTS_TO_LN = False 852 853 SUPPORTS_USER_DEFINED_TYPES = True 854 855 # Whether or not ADD is present for each column added by ALTER TABLE 856 ALTER_TABLE_ADD_COLUMN_KEYWORD = True 857 858 __slots__ = ( 859 "error_level", 860 "error_message_context", 861 "max_errors", 862 "sql", 863 "errors", 864 "_tokens", 865 "_index", 866 "_curr", 867 "_next", 868 "_prev", 869 "_prev_comments", 870 "_tokenizer", 871 ) 872 873 # Autofilled 874 TOKENIZER_CLASS: t.Type[Tokenizer] = Tokenizer 875 INDEX_OFFSET: int = 0 876 UNNEST_COLUMN_ONLY: bool = False 877 ALIAS_POST_TABLESAMPLE: bool = False 878 STRICT_STRING_CONCAT = False 879 NORMALIZE_FUNCTIONS = "upper" 880 NULL_ORDERING: str = "nulls_are_small" 881 SHOW_TRIE: t.Dict = {} 882 SET_TRIE: t.Dict = {} 883 FORMAT_MAPPING: t.Dict[str, str] = {} 884 FORMAT_TRIE: t.Dict = {} 885 TIME_MAPPING: t.Dict[str, str] = {} 886 TIME_TRIE: t.Dict = {} 887 888 def __init__( 889 self, 890 error_level: t.Optional[ErrorLevel] = None, 891 error_message_context: int = 100, 892 max_errors: int = 3, 893 ): 894 self.error_level = error_level or ErrorLevel.IMMEDIATE 895 self.error_message_context = error_message_context 896 self.max_errors = max_errors 897 self._tokenizer = self.TOKENIZER_CLASS() 898 self.reset() 899 900 def reset(self): 901 self.sql = "" 902 self.errors = [] 903 self._tokens = [] 904 self._index = 0 905 self._curr = None 906 self._next = None 907 self._prev = None 908 self._prev_comments = None 909 910 def parse( 911 self, raw_tokens: t.List[Token], sql: t.Optional[str] = None 912 ) -> t.List[t.Optional[exp.Expression]]: 913 """ 914 Parses a list of tokens and returns a list of syntax trees, one tree 915 per parsed SQL statement. 916 917 Args: 918 raw_tokens: The list of tokens. 919 sql: The original SQL string, used to produce helpful debug messages. 920 921 Returns: 922 The list of the produced syntax trees. 923 """ 924 return self._parse( 925 parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql 926 ) 927 928 def parse_into( 929 self, 930 expression_types: exp.IntoType, 931 raw_tokens: t.List[Token], 932 sql: t.Optional[str] = None, 933 ) -> t.List[t.Optional[exp.Expression]]: 934 """ 935 Parses a list of tokens into a given Expression type. If a collection of Expression 936 types is given instead, this method will try to parse the token list into each one 937 of them, stopping at the first for which the parsing succeeds. 938 939 Args: 940 expression_types: The expression type(s) to try and parse the token list into. 941 raw_tokens: The list of tokens. 942 sql: The original SQL string, used to produce helpful debug messages. 943 944 Returns: 945 The target Expression. 946 """ 947 errors = [] 948 for expression_type in ensure_list(expression_types): 949 parser = self.EXPRESSION_PARSERS.get(expression_type) 950 if not parser: 951 raise TypeError(f"No parser registered for {expression_type}") 952 953 try: 954 return self._parse(parser, raw_tokens, sql) 955 except ParseError as e: 956 e.errors[0]["into_expression"] = expression_type 957 errors.append(e) 958 959 raise ParseError( 960 f"Failed to parse '{sql or raw_tokens}' into {expression_types}", 961 errors=merge_errors(errors), 962 ) from errors[-1] 963 964 def _parse( 965 self, 966 parse_method: t.Callable[[Parser], t.Optional[exp.Expression]], 967 raw_tokens: t.List[Token], 968 sql: t.Optional[str] = None, 969 ) -> t.List[t.Optional[exp.Expression]]: 970 self.reset() 971 self.sql = sql or "" 972 973 total = len(raw_tokens) 974 chunks: t.List[t.List[Token]] = [[]] 975 976 for i, token in enumerate(raw_tokens): 977 if token.token_type == TokenType.SEMICOLON: 978 if i < total - 1: 979 chunks.append([]) 980 else: 981 chunks[-1].append(token) 982 983 expressions = [] 984 985 for tokens in chunks: 986 self._index = -1 987 self._tokens = tokens 988 self._advance() 989 990 expressions.append(parse_method(self)) 991 992 if self._index < len(self._tokens): 993 self.raise_error("Invalid expression / Unexpected token") 994 995 self.check_errors() 996 997 return expressions 998 999 def check_errors(self) -> None: 1000 """Logs or raises any found errors, depending on the chosen error level setting.""" 1001 if self.error_level == ErrorLevel.WARN: 1002 for error in self.errors: 1003 logger.error(str(error)) 1004 elif self.error_level == ErrorLevel.RAISE and self.errors: 1005 raise ParseError( 1006 concat_messages(self.errors, self.max_errors), 1007 errors=merge_errors(self.errors), 1008 ) 1009 1010 def raise_error(self, message: str, token: t.Optional[Token] = None) -> None: 1011 """ 1012 Appends an error in the list of recorded errors or raises it, depending on the chosen 1013 error level setting. 1014 """ 1015 token = token or self._curr or self._prev or Token.string("") 1016 start = token.start 1017 end = token.end + 1 1018 start_context = self.sql[max(start - self.error_message_context, 0) : start] 1019 highlight = self.sql[start:end] 1020 end_context = self.sql[end : end + self.error_message_context] 1021 1022 error = ParseError.new( 1023 f"{message}. Line {token.line}, Col: {token.col}.\n" 1024 f" {start_context}\033[4m{highlight}\033[0m{end_context}", 1025 description=message, 1026 line=token.line, 1027 col=token.col, 1028 start_context=start_context, 1029 highlight=highlight, 1030 end_context=end_context, 1031 ) 1032 1033 if self.error_level == ErrorLevel.IMMEDIATE: 1034 raise error 1035 1036 self.errors.append(error) 1037 1038 def expression( 1039 self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs 1040 ) -> E: 1041 """ 1042 Creates a new, validated Expression. 1043 1044 Args: 1045 exp_class: The expression class to instantiate. 1046 comments: An optional list of comments to attach to the expression. 1047 kwargs: The arguments to set for the expression along with their respective values. 1048 1049 Returns: 1050 The target expression. 1051 """ 1052 instance = exp_class(**kwargs) 1053 instance.add_comments(comments) if comments else self._add_comments(instance) 1054 return self.validate_expression(instance) 1055 1056 def _add_comments(self, expression: t.Optional[exp.Expression]) -> None: 1057 if expression and self._prev_comments: 1058 expression.add_comments(self._prev_comments) 1059 self._prev_comments = None 1060 1061 def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E: 1062 """ 1063 Validates an Expression, making sure that all its mandatory arguments are set. 1064 1065 Args: 1066 expression: The expression to validate. 1067 args: An optional list of items that was used to instantiate the expression, if it's a Func. 1068 1069 Returns: 1070 The validated expression. 1071 """ 1072 if self.error_level != ErrorLevel.IGNORE: 1073 for error_message in expression.error_messages(args): 1074 self.raise_error(error_message) 1075 1076 return expression 1077 1078 def _find_sql(self, start: Token, end: Token) -> str: 1079 return self.sql[start.start : end.end + 1] 1080 1081 def _advance(self, times: int = 1) -> None: 1082 self._index += times 1083 self._curr = seq_get(self._tokens, self._index) 1084 self._next = seq_get(self._tokens, self._index + 1) 1085 1086 if self._index > 0: 1087 self._prev = self._tokens[self._index - 1] 1088 self._prev_comments = self._prev.comments 1089 else: 1090 self._prev = None 1091 self._prev_comments = None 1092 1093 def _retreat(self, index: int) -> None: 1094 if index != self._index: 1095 self._advance(index - self._index) 1096 1097 def _parse_command(self) -> exp.Command: 1098 return self.expression(exp.Command, this=self._prev.text, expression=self._parse_string()) 1099 1100 def _parse_comment(self, allow_exists: bool = True) -> exp.Expression: 1101 start = self._prev 1102 exists = self._parse_exists() if allow_exists else None 1103 1104 self._match(TokenType.ON) 1105 1106 kind = self._match_set(self.CREATABLES) and self._prev 1107 if not kind: 1108 return self._parse_as_command(start) 1109 1110 if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE): 1111 this = self._parse_user_defined_function(kind=kind.token_type) 1112 elif kind.token_type == TokenType.TABLE: 1113 this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS) 1114 elif kind.token_type == TokenType.COLUMN: 1115 this = self._parse_column() 1116 else: 1117 this = self._parse_id_var() 1118 1119 self._match(TokenType.IS) 1120 1121 return self.expression( 1122 exp.Comment, this=this, kind=kind.text, expression=self._parse_string(), exists=exists 1123 ) 1124 1125 def _parse_to_table( 1126 self, 1127 ) -> exp.ToTableProperty: 1128 table = self._parse_table_parts(schema=True) 1129 return self.expression(exp.ToTableProperty, this=table) 1130 1131 # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl 1132 def _parse_ttl(self) -> exp.Expression: 1133 def _parse_ttl_action() -> t.Optional[exp.Expression]: 1134 this = self._parse_bitwise() 1135 1136 if self._match_text_seq("DELETE"): 1137 return self.expression(exp.MergeTreeTTLAction, this=this, delete=True) 1138 if self._match_text_seq("RECOMPRESS"): 1139 return self.expression( 1140 exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise() 1141 ) 1142 if self._match_text_seq("TO", "DISK"): 1143 return self.expression( 1144 exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string() 1145 ) 1146 if self._match_text_seq("TO", "VOLUME"): 1147 return self.expression( 1148 exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string() 1149 ) 1150 1151 return this 1152 1153 expressions = self._parse_csv(_parse_ttl_action) 1154 where = self._parse_where() 1155 group = self._parse_group() 1156 1157 aggregates = None 1158 if group and self._match(TokenType.SET): 1159 aggregates = self._parse_csv(self._parse_set_item) 1160 1161 return self.expression( 1162 exp.MergeTreeTTL, 1163 expressions=expressions, 1164 where=where, 1165 group=group, 1166 aggregates=aggregates, 1167 ) 1168 1169 def _parse_statement(self) -> t.Optional[exp.Expression]: 1170 if self._curr is None: 1171 return None 1172 1173 if self._match_set(self.STATEMENT_PARSERS): 1174 return self.STATEMENT_PARSERS[self._prev.token_type](self) 1175 1176 if self._match_set(Tokenizer.COMMANDS): 1177 return self._parse_command() 1178 1179 expression = self._parse_expression() 1180 expression = self._parse_set_operations(expression) if expression else self._parse_select() 1181 return self._parse_query_modifiers(expression) 1182 1183 def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command: 1184 start = self._prev 1185 temporary = self._match(TokenType.TEMPORARY) 1186 materialized = self._match_text_seq("MATERIALIZED") 1187 1188 kind = self._match_set(self.CREATABLES) and self._prev.text 1189 if not kind: 1190 return self._parse_as_command(start) 1191 1192 return self.expression( 1193 exp.Drop, 1194 comments=start.comments, 1195 exists=exists or self._parse_exists(), 1196 this=self._parse_table(schema=True), 1197 kind=kind, 1198 temporary=temporary, 1199 materialized=materialized, 1200 cascade=self._match_text_seq("CASCADE"), 1201 constraints=self._match_text_seq("CONSTRAINTS"), 1202 purge=self._match_text_seq("PURGE"), 1203 ) 1204 1205 def _parse_exists(self, not_: bool = False) -> t.Optional[bool]: 1206 return ( 1207 self._match_text_seq("IF") 1208 and (not not_ or self._match(TokenType.NOT)) 1209 and self._match(TokenType.EXISTS) 1210 ) 1211 1212 def _parse_create(self) -> exp.Create | exp.Command: 1213 # Note: this can't be None because we've matched a statement parser 1214 start = self._prev 1215 comments = self._prev_comments 1216 1217 replace = start.text.upper() == "REPLACE" or self._match_pair( 1218 TokenType.OR, TokenType.REPLACE 1219 ) 1220 unique = self._match(TokenType.UNIQUE) 1221 1222 if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False): 1223 self._advance() 1224 1225 properties = None 1226 create_token = self._match_set(self.CREATABLES) and self._prev 1227 1228 if not create_token: 1229 # exp.Properties.Location.POST_CREATE 1230 properties = self._parse_properties() 1231 create_token = self._match_set(self.CREATABLES) and self._prev 1232 1233 if not properties or not create_token: 1234 return self._parse_as_command(start) 1235 1236 exists = self._parse_exists(not_=True) 1237 this = None 1238 expression: t.Optional[exp.Expression] = None 1239 indexes = None 1240 no_schema_binding = None 1241 begin = None 1242 clone = None 1243 1244 def extend_props(temp_props: t.Optional[exp.Properties]) -> None: 1245 nonlocal properties 1246 if properties and temp_props: 1247 properties.expressions.extend(temp_props.expressions) 1248 elif temp_props: 1249 properties = temp_props 1250 1251 if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE): 1252 this = self._parse_user_defined_function(kind=create_token.token_type) 1253 1254 # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature) 1255 extend_props(self._parse_properties()) 1256 1257 self._match(TokenType.ALIAS) 1258 1259 if self._match(TokenType.COMMAND): 1260 expression = self._parse_as_command(self._prev) 1261 else: 1262 begin = self._match(TokenType.BEGIN) 1263 return_ = self._match_text_seq("RETURN") 1264 expression = self._parse_statement() 1265 1266 if return_: 1267 expression = self.expression(exp.Return, this=expression) 1268 elif create_token.token_type == TokenType.INDEX: 1269 this = self._parse_index(index=self._parse_id_var()) 1270 elif create_token.token_type in self.DB_CREATABLES: 1271 table_parts = self._parse_table_parts(schema=True) 1272 1273 # exp.Properties.Location.POST_NAME 1274 self._match(TokenType.COMMA) 1275 extend_props(self._parse_properties(before=True)) 1276 1277 this = self._parse_schema(this=table_parts) 1278 1279 # exp.Properties.Location.POST_SCHEMA and POST_WITH 1280 extend_props(self._parse_properties()) 1281 1282 self._match(TokenType.ALIAS) 1283 if not self._match_set(self.DDL_SELECT_TOKENS, advance=False): 1284 # exp.Properties.Location.POST_ALIAS 1285 extend_props(self._parse_properties()) 1286 1287 expression = self._parse_ddl_select() 1288 1289 if create_token.token_type == TokenType.TABLE: 1290 # exp.Properties.Location.POST_EXPRESSION 1291 extend_props(self._parse_properties()) 1292 1293 indexes = [] 1294 while True: 1295 index = self._parse_index() 1296 1297 # exp.Properties.Location.POST_INDEX 1298 extend_props(self._parse_properties()) 1299 1300 if not index: 1301 break 1302 else: 1303 self._match(TokenType.COMMA) 1304 indexes.append(index) 1305 elif create_token.token_type == TokenType.VIEW: 1306 if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"): 1307 no_schema_binding = True 1308 1309 shallow = self._match_text_seq("SHALLOW") 1310 1311 if self._match_text_seq("CLONE"): 1312 clone = self._parse_table(schema=True) 1313 when = self._match_texts({"AT", "BEFORE"}) and self._prev.text.upper() 1314 clone_kind = ( 1315 self._match(TokenType.L_PAREN) 1316 and self._match_texts(self.CLONE_KINDS) 1317 and self._prev.text.upper() 1318 ) 1319 clone_expression = self._match(TokenType.FARROW) and self._parse_bitwise() 1320 self._match(TokenType.R_PAREN) 1321 clone = self.expression( 1322 exp.Clone, 1323 this=clone, 1324 when=when, 1325 kind=clone_kind, 1326 shallow=shallow, 1327 expression=clone_expression, 1328 ) 1329 1330 return self.expression( 1331 exp.Create, 1332 comments=comments, 1333 this=this, 1334 kind=create_token.text, 1335 replace=replace, 1336 unique=unique, 1337 expression=expression, 1338 exists=exists, 1339 properties=properties, 1340 indexes=indexes, 1341 no_schema_binding=no_schema_binding, 1342 begin=begin, 1343 clone=clone, 1344 ) 1345 1346 def _parse_property_before(self) -> t.Optional[exp.Expression]: 1347 # only used for teradata currently 1348 self._match(TokenType.COMMA) 1349 1350 kwargs = { 1351 "no": self._match_text_seq("NO"), 1352 "dual": self._match_text_seq("DUAL"), 1353 "before": self._match_text_seq("BEFORE"), 1354 "default": self._match_text_seq("DEFAULT"), 1355 "local": (self._match_text_seq("LOCAL") and "LOCAL") 1356 or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"), 1357 "after": self._match_text_seq("AFTER"), 1358 "minimum": self._match_texts(("MIN", "MINIMUM")), 1359 "maximum": self._match_texts(("MAX", "MAXIMUM")), 1360 } 1361 1362 if self._match_texts(self.PROPERTY_PARSERS): 1363 parser = self.PROPERTY_PARSERS[self._prev.text.upper()] 1364 try: 1365 return parser(self, **{k: v for k, v in kwargs.items() if v}) 1366 except TypeError: 1367 self.raise_error(f"Cannot parse property '{self._prev.text}'") 1368 1369 return None 1370 1371 def _parse_property(self) -> t.Optional[exp.Expression]: 1372 if self._match_texts(self.PROPERTY_PARSERS): 1373 return self.PROPERTY_PARSERS[self._prev.text.upper()](self) 1374 1375 if self._match_pair(TokenType.DEFAULT, TokenType.CHARACTER_SET): 1376 return self._parse_character_set(default=True) 1377 1378 if self._match_text_seq("COMPOUND", "SORTKEY"): 1379 return self._parse_sortkey(compound=True) 1380 1381 if self._match_text_seq("SQL", "SECURITY"): 1382 return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER")) 1383 1384 assignment = self._match_pair( 1385 TokenType.VAR, TokenType.EQ, advance=False 1386 ) or self._match_pair(TokenType.STRING, TokenType.EQ, advance=False) 1387 1388 if assignment: 1389 key = self._parse_var_or_string() 1390 self._match(TokenType.EQ) 1391 return self.expression( 1392 exp.Property, 1393 this=key, 1394 value=self._parse_column() or self._parse_var(any_token=True), 1395 ) 1396 1397 return None 1398 1399 def _parse_stored(self) -> exp.FileFormatProperty: 1400 self._match(TokenType.ALIAS) 1401 1402 input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None 1403 output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None 1404 1405 return self.expression( 1406 exp.FileFormatProperty, 1407 this=self.expression( 1408 exp.InputOutputFormat, input_format=input_format, output_format=output_format 1409 ) 1410 if input_format or output_format 1411 else self._parse_var_or_string() or self._parse_number() or self._parse_id_var(), 1412 ) 1413 1414 def _parse_property_assignment(self, exp_class: t.Type[E]) -> E: 1415 self._match(TokenType.EQ) 1416 self._match(TokenType.ALIAS) 1417 return self.expression(exp_class, this=self._parse_field()) 1418 1419 def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]: 1420 properties = [] 1421 while True: 1422 if before: 1423 prop = self._parse_property_before() 1424 else: 1425 prop = self._parse_property() 1426 1427 if not prop: 1428 break 1429 for p in ensure_list(prop): 1430 properties.append(p) 1431 1432 if properties: 1433 return self.expression(exp.Properties, expressions=properties) 1434 1435 return None 1436 1437 def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty: 1438 return self.expression( 1439 exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION") 1440 ) 1441 1442 def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty: 1443 if self._index >= 2: 1444 pre_volatile_token = self._tokens[self._index - 2] 1445 else: 1446 pre_volatile_token = None 1447 1448 if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS: 1449 return exp.VolatileProperty() 1450 1451 return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE")) 1452 1453 def _parse_with_property( 1454 self, 1455 ) -> t.Optional[exp.Expression] | t.List[exp.Expression]: 1456 if self._match(TokenType.L_PAREN, advance=False): 1457 return self._parse_wrapped_csv(self._parse_property) 1458 1459 if self._match_text_seq("JOURNAL"): 1460 return self._parse_withjournaltable() 1461 1462 if self._match_text_seq("DATA"): 1463 return self._parse_withdata(no=False) 1464 elif self._match_text_seq("NO", "DATA"): 1465 return self._parse_withdata(no=True) 1466 1467 if not self._next: 1468 return None 1469 1470 return self._parse_withisolatedloading() 1471 1472 # https://dev.mysql.com/doc/refman/8.0/en/create-view.html 1473 def _parse_definer(self) -> t.Optional[exp.DefinerProperty]: 1474 self._match(TokenType.EQ) 1475 1476 user = self._parse_id_var() 1477 self._match(TokenType.PARAMETER) 1478 host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text) 1479 1480 if not user or not host: 1481 return None 1482 1483 return exp.DefinerProperty(this=f"{user}@{host}") 1484 1485 def _parse_withjournaltable(self) -> exp.WithJournalTableProperty: 1486 self._match(TokenType.TABLE) 1487 self._match(TokenType.EQ) 1488 return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts()) 1489 1490 def _parse_log(self, no: bool = False) -> exp.LogProperty: 1491 return self.expression(exp.LogProperty, no=no) 1492 1493 def _parse_journal(self, **kwargs) -> exp.JournalProperty: 1494 return self.expression(exp.JournalProperty, **kwargs) 1495 1496 def _parse_checksum(self) -> exp.ChecksumProperty: 1497 self._match(TokenType.EQ) 1498 1499 on = None 1500 if self._match(TokenType.ON): 1501 on = True 1502 elif self._match_text_seq("OFF"): 1503 on = False 1504 1505 return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT)) 1506 1507 def _parse_cluster(self) -> exp.Cluster: 1508 return self.expression(exp.Cluster, expressions=self._parse_csv(self._parse_ordered)) 1509 1510 def _parse_clustered_by(self) -> exp.ClusteredByProperty: 1511 self._match_text_seq("BY") 1512 1513 self._match_l_paren() 1514 expressions = self._parse_csv(self._parse_column) 1515 self._match_r_paren() 1516 1517 if self._match_text_seq("SORTED", "BY"): 1518 self._match_l_paren() 1519 sorted_by = self._parse_csv(self._parse_ordered) 1520 self._match_r_paren() 1521 else: 1522 sorted_by = None 1523 1524 self._match(TokenType.INTO) 1525 buckets = self._parse_number() 1526 self._match_text_seq("BUCKETS") 1527 1528 return self.expression( 1529 exp.ClusteredByProperty, 1530 expressions=expressions, 1531 sorted_by=sorted_by, 1532 buckets=buckets, 1533 ) 1534 1535 def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]: 1536 if not self._match_text_seq("GRANTS"): 1537 self._retreat(self._index - 1) 1538 return None 1539 1540 return self.expression(exp.CopyGrantsProperty) 1541 1542 def _parse_freespace(self) -> exp.FreespaceProperty: 1543 self._match(TokenType.EQ) 1544 return self.expression( 1545 exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT) 1546 ) 1547 1548 def _parse_mergeblockratio( 1549 self, no: bool = False, default: bool = False 1550 ) -> exp.MergeBlockRatioProperty: 1551 if self._match(TokenType.EQ): 1552 return self.expression( 1553 exp.MergeBlockRatioProperty, 1554 this=self._parse_number(), 1555 percent=self._match(TokenType.PERCENT), 1556 ) 1557 1558 return self.expression(exp.MergeBlockRatioProperty, no=no, default=default) 1559 1560 def _parse_datablocksize( 1561 self, 1562 default: t.Optional[bool] = None, 1563 minimum: t.Optional[bool] = None, 1564 maximum: t.Optional[bool] = None, 1565 ) -> exp.DataBlocksizeProperty: 1566 self._match(TokenType.EQ) 1567 size = self._parse_number() 1568 1569 units = None 1570 if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")): 1571 units = self._prev.text 1572 1573 return self.expression( 1574 exp.DataBlocksizeProperty, 1575 size=size, 1576 units=units, 1577 default=default, 1578 minimum=minimum, 1579 maximum=maximum, 1580 ) 1581 1582 def _parse_blockcompression(self) -> exp.BlockCompressionProperty: 1583 self._match(TokenType.EQ) 1584 always = self._match_text_seq("ALWAYS") 1585 manual = self._match_text_seq("MANUAL") 1586 never = self._match_text_seq("NEVER") 1587 default = self._match_text_seq("DEFAULT") 1588 1589 autotemp = None 1590 if self._match_text_seq("AUTOTEMP"): 1591 autotemp = self._parse_schema() 1592 1593 return self.expression( 1594 exp.BlockCompressionProperty, 1595 always=always, 1596 manual=manual, 1597 never=never, 1598 default=default, 1599 autotemp=autotemp, 1600 ) 1601 1602 def _parse_withisolatedloading(self) -> exp.IsolatedLoadingProperty: 1603 no = self._match_text_seq("NO") 1604 concurrent = self._match_text_seq("CONCURRENT") 1605 self._match_text_seq("ISOLATED", "LOADING") 1606 for_all = self._match_text_seq("FOR", "ALL") 1607 for_insert = self._match_text_seq("FOR", "INSERT") 1608 for_none = self._match_text_seq("FOR", "NONE") 1609 return self.expression( 1610 exp.IsolatedLoadingProperty, 1611 no=no, 1612 concurrent=concurrent, 1613 for_all=for_all, 1614 for_insert=for_insert, 1615 for_none=for_none, 1616 ) 1617 1618 def _parse_locking(self) -> exp.LockingProperty: 1619 if self._match(TokenType.TABLE): 1620 kind = "TABLE" 1621 elif self._match(TokenType.VIEW): 1622 kind = "VIEW" 1623 elif self._match(TokenType.ROW): 1624 kind = "ROW" 1625 elif self._match_text_seq("DATABASE"): 1626 kind = "DATABASE" 1627 else: 1628 kind = None 1629 1630 if kind in ("DATABASE", "TABLE", "VIEW"): 1631 this = self._parse_table_parts() 1632 else: 1633 this = None 1634 1635 if self._match(TokenType.FOR): 1636 for_or_in = "FOR" 1637 elif self._match(TokenType.IN): 1638 for_or_in = "IN" 1639 else: 1640 for_or_in = None 1641 1642 if self._match_text_seq("ACCESS"): 1643 lock_type = "ACCESS" 1644 elif self._match_texts(("EXCL", "EXCLUSIVE")): 1645 lock_type = "EXCLUSIVE" 1646 elif self._match_text_seq("SHARE"): 1647 lock_type = "SHARE" 1648 elif self._match_text_seq("READ"): 1649 lock_type = "READ" 1650 elif self._match_text_seq("WRITE"): 1651 lock_type = "WRITE" 1652 elif self._match_text_seq("CHECKSUM"): 1653 lock_type = "CHECKSUM" 1654 else: 1655 lock_type = None 1656 1657 override = self._match_text_seq("OVERRIDE") 1658 1659 return self.expression( 1660 exp.LockingProperty, 1661 this=this, 1662 kind=kind, 1663 for_or_in=for_or_in, 1664 lock_type=lock_type, 1665 override=override, 1666 ) 1667 1668 def _parse_partition_by(self) -> t.List[exp.Expression]: 1669 if self._match(TokenType.PARTITION_BY): 1670 return self._parse_csv(self._parse_conjunction) 1671 return [] 1672 1673 def _parse_partitioned_by(self) -> exp.PartitionedByProperty: 1674 self._match(TokenType.EQ) 1675 return self.expression( 1676 exp.PartitionedByProperty, 1677 this=self._parse_schema() or self._parse_bracket(self._parse_field()), 1678 ) 1679 1680 def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty: 1681 if self._match_text_seq("AND", "STATISTICS"): 1682 statistics = True 1683 elif self._match_text_seq("AND", "NO", "STATISTICS"): 1684 statistics = False 1685 else: 1686 statistics = None 1687 1688 return self.expression(exp.WithDataProperty, no=no, statistics=statistics) 1689 1690 def _parse_no_property(self) -> t.Optional[exp.NoPrimaryIndexProperty]: 1691 if self._match_text_seq("PRIMARY", "INDEX"): 1692 return exp.NoPrimaryIndexProperty() 1693 return None 1694 1695 def _parse_on_property(self) -> t.Optional[exp.Expression]: 1696 if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"): 1697 return exp.OnCommitProperty() 1698 if self._match_text_seq("COMMIT", "DELETE", "ROWS"): 1699 return exp.OnCommitProperty(delete=True) 1700 return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var())) 1701 1702 def _parse_distkey(self) -> exp.DistKeyProperty: 1703 return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var)) 1704 1705 def _parse_create_like(self) -> t.Optional[exp.LikeProperty]: 1706 table = self._parse_table(schema=True) 1707 1708 options = [] 1709 while self._match_texts(("INCLUDING", "EXCLUDING")): 1710 this = self._prev.text.upper() 1711 1712 id_var = self._parse_id_var() 1713 if not id_var: 1714 return None 1715 1716 options.append( 1717 self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper())) 1718 ) 1719 1720 return self.expression(exp.LikeProperty, this=table, expressions=options) 1721 1722 def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty: 1723 return self.expression( 1724 exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound 1725 ) 1726 1727 def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty: 1728 self._match(TokenType.EQ) 1729 return self.expression( 1730 exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default 1731 ) 1732 1733 def _parse_returns(self) -> exp.ReturnsProperty: 1734 value: t.Optional[exp.Expression] 1735 is_table = self._match(TokenType.TABLE) 1736 1737 if is_table: 1738 if self._match(TokenType.LT): 1739 value = self.expression( 1740 exp.Schema, 1741 this="TABLE", 1742 expressions=self._parse_csv(self._parse_struct_types), 1743 ) 1744 if not self._match(TokenType.GT): 1745 self.raise_error("Expecting >") 1746 else: 1747 value = self._parse_schema(exp.var("TABLE")) 1748 else: 1749 value = self._parse_types() 1750 1751 return self.expression(exp.ReturnsProperty, this=value, is_table=is_table) 1752 1753 def _parse_describe(self) -> exp.Describe: 1754 kind = self._match_set(self.CREATABLES) and self._prev.text 1755 this = self._parse_table() 1756 return self.expression(exp.Describe, this=this, kind=kind) 1757 1758 def _parse_insert(self) -> exp.Insert: 1759 comments = ensure_list(self._prev_comments) 1760 overwrite = self._match(TokenType.OVERWRITE) 1761 ignore = self._match(TokenType.IGNORE) 1762 local = self._match_text_seq("LOCAL") 1763 alternative = None 1764 1765 if self._match_text_seq("DIRECTORY"): 1766 this: t.Optional[exp.Expression] = self.expression( 1767 exp.Directory, 1768 this=self._parse_var_or_string(), 1769 local=local, 1770 row_format=self._parse_row_format(match_row=True), 1771 ) 1772 else: 1773 if self._match(TokenType.OR): 1774 alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text 1775 1776 self._match(TokenType.INTO) 1777 comments += ensure_list(self._prev_comments) 1778 self._match(TokenType.TABLE) 1779 this = self._parse_table(schema=True) 1780 1781 returning = self._parse_returning() 1782 1783 return self.expression( 1784 exp.Insert, 1785 comments=comments, 1786 this=this, 1787 by_name=self._match_text_seq("BY", "NAME"), 1788 exists=self._parse_exists(), 1789 partition=self._parse_partition(), 1790 where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) 1791 and self._parse_conjunction(), 1792 expression=self._parse_ddl_select(), 1793 conflict=self._parse_on_conflict(), 1794 returning=returning or self._parse_returning(), 1795 overwrite=overwrite, 1796 alternative=alternative, 1797 ignore=ignore, 1798 ) 1799 1800 def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]: 1801 conflict = self._match_text_seq("ON", "CONFLICT") 1802 duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY") 1803 1804 if not conflict and not duplicate: 1805 return None 1806 1807 nothing = None 1808 expressions = None 1809 key = None 1810 constraint = None 1811 1812 if conflict: 1813 if self._match_text_seq("ON", "CONSTRAINT"): 1814 constraint = self._parse_id_var() 1815 else: 1816 key = self._parse_csv(self._parse_value) 1817 1818 self._match_text_seq("DO") 1819 if self._match_text_seq("NOTHING"): 1820 nothing = True 1821 else: 1822 self._match(TokenType.UPDATE) 1823 self._match(TokenType.SET) 1824 expressions = self._parse_csv(self._parse_equality) 1825 1826 return self.expression( 1827 exp.OnConflict, 1828 duplicate=duplicate, 1829 expressions=expressions, 1830 nothing=nothing, 1831 key=key, 1832 constraint=constraint, 1833 ) 1834 1835 def _parse_returning(self) -> t.Optional[exp.Returning]: 1836 if not self._match(TokenType.RETURNING): 1837 return None 1838 return self.expression( 1839 exp.Returning, 1840 expressions=self._parse_csv(self._parse_expression), 1841 into=self._match(TokenType.INTO) and self._parse_table_part(), 1842 ) 1843 1844 def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]: 1845 if not self._match(TokenType.FORMAT): 1846 return None 1847 return self._parse_row_format() 1848 1849 def _parse_row_format( 1850 self, match_row: bool = False 1851 ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]: 1852 if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT): 1853 return None 1854 1855 if self._match_text_seq("SERDE"): 1856 this = self._parse_string() 1857 1858 serde_properties = None 1859 if self._match(TokenType.SERDE_PROPERTIES): 1860 serde_properties = self.expression( 1861 exp.SerdeProperties, expressions=self._parse_wrapped_csv(self._parse_property) 1862 ) 1863 1864 return self.expression( 1865 exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties 1866 ) 1867 1868 self._match_text_seq("DELIMITED") 1869 1870 kwargs = {} 1871 1872 if self._match_text_seq("FIELDS", "TERMINATED", "BY"): 1873 kwargs["fields"] = self._parse_string() 1874 if self._match_text_seq("ESCAPED", "BY"): 1875 kwargs["escaped"] = self._parse_string() 1876 if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"): 1877 kwargs["collection_items"] = self._parse_string() 1878 if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"): 1879 kwargs["map_keys"] = self._parse_string() 1880 if self._match_text_seq("LINES", "TERMINATED", "BY"): 1881 kwargs["lines"] = self._parse_string() 1882 if self._match_text_seq("NULL", "DEFINED", "AS"): 1883 kwargs["null"] = self._parse_string() 1884 1885 return self.expression(exp.RowFormatDelimitedProperty, **kwargs) # type: ignore 1886 1887 def _parse_load(self) -> exp.LoadData | exp.Command: 1888 if self._match_text_seq("DATA"): 1889 local = self._match_text_seq("LOCAL") 1890 self._match_text_seq("INPATH") 1891 inpath = self._parse_string() 1892 overwrite = self._match(TokenType.OVERWRITE) 1893 self._match_pair(TokenType.INTO, TokenType.TABLE) 1894 1895 return self.expression( 1896 exp.LoadData, 1897 this=self._parse_table(schema=True), 1898 local=local, 1899 overwrite=overwrite, 1900 inpath=inpath, 1901 partition=self._parse_partition(), 1902 input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(), 1903 serde=self._match_text_seq("SERDE") and self._parse_string(), 1904 ) 1905 return self._parse_as_command(self._prev) 1906 1907 def _parse_delete(self) -> exp.Delete: 1908 # This handles MySQL's "Multiple-Table Syntax" 1909 # https://dev.mysql.com/doc/refman/8.0/en/delete.html 1910 tables = None 1911 comments = self._prev_comments 1912 if not self._match(TokenType.FROM, advance=False): 1913 tables = self._parse_csv(self._parse_table) or None 1914 1915 returning = self._parse_returning() 1916 1917 return self.expression( 1918 exp.Delete, 1919 comments=comments, 1920 tables=tables, 1921 this=self._match(TokenType.FROM) and self._parse_table(joins=True), 1922 using=self._match(TokenType.USING) and self._parse_table(joins=True), 1923 where=self._parse_where(), 1924 returning=returning or self._parse_returning(), 1925 limit=self._parse_limit(), 1926 ) 1927 1928 def _parse_update(self) -> exp.Update: 1929 comments = self._prev_comments 1930 this = self._parse_table(alias_tokens=self.UPDATE_ALIAS_TOKENS) 1931 expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality) 1932 returning = self._parse_returning() 1933 return self.expression( 1934 exp.Update, 1935 comments=comments, 1936 **{ # type: ignore 1937 "this": this, 1938 "expressions": expressions, 1939 "from": self._parse_from(joins=True), 1940 "where": self._parse_where(), 1941 "returning": returning or self._parse_returning(), 1942 "order": self._parse_order(), 1943 "limit": self._parse_limit(), 1944 }, 1945 ) 1946 1947 def _parse_uncache(self) -> exp.Uncache: 1948 if not self._match(TokenType.TABLE): 1949 self.raise_error("Expecting TABLE after UNCACHE") 1950 1951 return self.expression( 1952 exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True) 1953 ) 1954 1955 def _parse_cache(self) -> exp.Cache: 1956 lazy = self._match_text_seq("LAZY") 1957 self._match(TokenType.TABLE) 1958 table = self._parse_table(schema=True) 1959 1960 options = [] 1961 if self._match_text_seq("OPTIONS"): 1962 self._match_l_paren() 1963 k = self._parse_string() 1964 self._match(TokenType.EQ) 1965 v = self._parse_string() 1966 options = [k, v] 1967 self._match_r_paren() 1968 1969 self._match(TokenType.ALIAS) 1970 return self.expression( 1971 exp.Cache, 1972 this=table, 1973 lazy=lazy, 1974 options=options, 1975 expression=self._parse_select(nested=True), 1976 ) 1977 1978 def _parse_partition(self) -> t.Optional[exp.Partition]: 1979 if not self._match(TokenType.PARTITION): 1980 return None 1981 1982 return self.expression( 1983 exp.Partition, expressions=self._parse_wrapped_csv(self._parse_conjunction) 1984 ) 1985 1986 def _parse_value(self) -> exp.Tuple: 1987 if self._match(TokenType.L_PAREN): 1988 expressions = self._parse_csv(self._parse_conjunction) 1989 self._match_r_paren() 1990 return self.expression(exp.Tuple, expressions=expressions) 1991 1992 # In presto we can have VALUES 1, 2 which results in 1 column & 2 rows. 1993 # https://prestodb.io/docs/current/sql/values.html 1994 return self.expression(exp.Tuple, expressions=[self._parse_conjunction()]) 1995 1996 def _parse_projections(self) -> t.List[exp.Expression]: 1997 return self._parse_expressions() 1998 1999 def _parse_select( 2000 self, nested: bool = False, table: bool = False, parse_subquery_alias: bool = True 2001 ) -> t.Optional[exp.Expression]: 2002 cte = self._parse_with() 2003 2004 if cte: 2005 this = self._parse_statement() 2006 2007 if not this: 2008 self.raise_error("Failed to parse any statement following CTE") 2009 return cte 2010 2011 if "with" in this.arg_types: 2012 this.set("with", cte) 2013 else: 2014 self.raise_error(f"{this.key} does not support CTE") 2015 this = cte 2016 2017 return this 2018 2019 # duckdb supports leading with FROM x 2020 from_ = self._parse_from() if self._match(TokenType.FROM, advance=False) else None 2021 2022 if self._match(TokenType.SELECT): 2023 comments = self._prev_comments 2024 2025 hint = self._parse_hint() 2026 all_ = self._match(TokenType.ALL) 2027 distinct = self._match_set(self.DISTINCT_TOKENS) 2028 2029 kind = ( 2030 self._match(TokenType.ALIAS) 2031 and self._match_texts(("STRUCT", "VALUE")) 2032 and self._prev.text 2033 ) 2034 2035 if distinct: 2036 distinct = self.expression( 2037 exp.Distinct, 2038 on=self._parse_value() if self._match(TokenType.ON) else None, 2039 ) 2040 2041 if all_ and distinct: 2042 self.raise_error("Cannot specify both ALL and DISTINCT after SELECT") 2043 2044 limit = self._parse_limit(top=True) 2045 projections = self._parse_projections() 2046 2047 this = self.expression( 2048 exp.Select, 2049 kind=kind, 2050 hint=hint, 2051 distinct=distinct, 2052 expressions=projections, 2053 limit=limit, 2054 ) 2055 this.comments = comments 2056 2057 into = self._parse_into() 2058 if into: 2059 this.set("into", into) 2060 2061 if not from_: 2062 from_ = self._parse_from() 2063 2064 if from_: 2065 this.set("from", from_) 2066 2067 this = self._parse_query_modifiers(this) 2068 elif (table or nested) and self._match(TokenType.L_PAREN): 2069 if self._match(TokenType.PIVOT): 2070 this = self._parse_simplified_pivot() 2071 elif self._match(TokenType.FROM): 2072 this = exp.select("*").from_( 2073 t.cast(exp.From, self._parse_from(skip_from_token=True)) 2074 ) 2075 else: 2076 this = self._parse_table() if table else self._parse_select(nested=True) 2077 this = self._parse_set_operations(self._parse_query_modifiers(this)) 2078 2079 self._match_r_paren() 2080 2081 # We return early here so that the UNION isn't attached to the subquery by the 2082 # following call to _parse_set_operations, but instead becomes the parent node 2083 return self._parse_subquery(this, parse_alias=parse_subquery_alias) 2084 elif self._match(TokenType.VALUES): 2085 this = self.expression( 2086 exp.Values, 2087 expressions=self._parse_csv(self._parse_value), 2088 alias=self._parse_table_alias(), 2089 ) 2090 elif from_: 2091 this = exp.select("*").from_(from_.this, copy=False) 2092 else: 2093 this = None 2094 2095 return self._parse_set_operations(this) 2096 2097 def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]: 2098 if not skip_with_token and not self._match(TokenType.WITH): 2099 return None 2100 2101 comments = self._prev_comments 2102 recursive = self._match(TokenType.RECURSIVE) 2103 2104 expressions = [] 2105 while True: 2106 expressions.append(self._parse_cte()) 2107 2108 if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH): 2109 break 2110 else: 2111 self._match(TokenType.WITH) 2112 2113 return self.expression( 2114 exp.With, comments=comments, expressions=expressions, recursive=recursive 2115 ) 2116 2117 def _parse_cte(self) -> exp.CTE: 2118 alias = self._parse_table_alias() 2119 if not alias or not alias.this: 2120 self.raise_error("Expected CTE to have alias") 2121 2122 self._match(TokenType.ALIAS) 2123 return self.expression( 2124 exp.CTE, this=self._parse_wrapped(self._parse_statement), alias=alias 2125 ) 2126 2127 def _parse_table_alias( 2128 self, alias_tokens: t.Optional[t.Collection[TokenType]] = None 2129 ) -> t.Optional[exp.TableAlias]: 2130 any_token = self._match(TokenType.ALIAS) 2131 alias = ( 2132 self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS) 2133 or self._parse_string_as_identifier() 2134 ) 2135 2136 index = self._index 2137 if self._match(TokenType.L_PAREN): 2138 columns = self._parse_csv(self._parse_function_parameter) 2139 self._match_r_paren() if columns else self._retreat(index) 2140 else: 2141 columns = None 2142 2143 if not alias and not columns: 2144 return None 2145 2146 return self.expression(exp.TableAlias, this=alias, columns=columns) 2147 2148 def _parse_subquery( 2149 self, this: t.Optional[exp.Expression], parse_alias: bool = True 2150 ) -> t.Optional[exp.Subquery]: 2151 if not this: 2152 return None 2153 2154 return self.expression( 2155 exp.Subquery, 2156 this=this, 2157 pivots=self._parse_pivots(), 2158 alias=self._parse_table_alias() if parse_alias else None, 2159 ) 2160 2161 def _parse_query_modifiers( 2162 self, this: t.Optional[exp.Expression] 2163 ) -> t.Optional[exp.Expression]: 2164 if isinstance(this, self.MODIFIABLES): 2165 for join in iter(self._parse_join, None): 2166 this.append("joins", join) 2167 for lateral in iter(self._parse_lateral, None): 2168 this.append("laterals", lateral) 2169 2170 while True: 2171 if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False): 2172 parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type] 2173 key, expression = parser(self) 2174 2175 if expression: 2176 this.set(key, expression) 2177 if key == "limit": 2178 offset = expression.args.pop("offset", None) 2179 if offset: 2180 this.set("offset", exp.Offset(expression=offset)) 2181 continue 2182 break 2183 return this 2184 2185 def _parse_hint(self) -> t.Optional[exp.Hint]: 2186 if self._match(TokenType.HINT): 2187 hints = [] 2188 for hint in iter(lambda: self._parse_csv(self._parse_function), []): 2189 hints.extend(hint) 2190 2191 if not self._match_pair(TokenType.STAR, TokenType.SLASH): 2192 self.raise_error("Expected */ after HINT") 2193 2194 return self.expression(exp.Hint, expressions=hints) 2195 2196 return None 2197 2198 def _parse_into(self) -> t.Optional[exp.Into]: 2199 if not self._match(TokenType.INTO): 2200 return None 2201 2202 temp = self._match(TokenType.TEMPORARY) 2203 unlogged = self._match_text_seq("UNLOGGED") 2204 self._match(TokenType.TABLE) 2205 2206 return self.expression( 2207 exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged 2208 ) 2209 2210 def _parse_from( 2211 self, joins: bool = False, skip_from_token: bool = False 2212 ) -> t.Optional[exp.From]: 2213 if not skip_from_token and not self._match(TokenType.FROM): 2214 return None 2215 2216 return self.expression( 2217 exp.From, comments=self._prev_comments, this=self._parse_table(joins=joins) 2218 ) 2219 2220 def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]: 2221 if not self._match(TokenType.MATCH_RECOGNIZE): 2222 return None 2223 2224 self._match_l_paren() 2225 2226 partition = self._parse_partition_by() 2227 order = self._parse_order() 2228 measures = self._parse_expressions() if self._match_text_seq("MEASURES") else None 2229 2230 if self._match_text_seq("ONE", "ROW", "PER", "MATCH"): 2231 rows = exp.var("ONE ROW PER MATCH") 2232 elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"): 2233 text = "ALL ROWS PER MATCH" 2234 if self._match_text_seq("SHOW", "EMPTY", "MATCHES"): 2235 text += f" SHOW EMPTY MATCHES" 2236 elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"): 2237 text += f" OMIT EMPTY MATCHES" 2238 elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"): 2239 text += f" WITH UNMATCHED ROWS" 2240 rows = exp.var(text) 2241 else: 2242 rows = None 2243 2244 if self._match_text_seq("AFTER", "MATCH", "SKIP"): 2245 text = "AFTER MATCH SKIP" 2246 if self._match_text_seq("PAST", "LAST", "ROW"): 2247 text += f" PAST LAST ROW" 2248 elif self._match_text_seq("TO", "NEXT", "ROW"): 2249 text += f" TO NEXT ROW" 2250 elif self._match_text_seq("TO", "FIRST"): 2251 text += f" TO FIRST {self._advance_any().text}" # type: ignore 2252 elif self._match_text_seq("TO", "LAST"): 2253 text += f" TO LAST {self._advance_any().text}" # type: ignore 2254 after = exp.var(text) 2255 else: 2256 after = None 2257 2258 if self._match_text_seq("PATTERN"): 2259 self._match_l_paren() 2260 2261 if not self._curr: 2262 self.raise_error("Expecting )", self._curr) 2263 2264 paren = 1 2265 start = self._curr 2266 2267 while self._curr and paren > 0: 2268 if self._curr.token_type == TokenType.L_PAREN: 2269 paren += 1 2270 if self._curr.token_type == TokenType.R_PAREN: 2271 paren -= 1 2272 2273 end = self._prev 2274 self._advance() 2275 2276 if paren > 0: 2277 self.raise_error("Expecting )", self._curr) 2278 2279 pattern = exp.var(self._find_sql(start, end)) 2280 else: 2281 pattern = None 2282 2283 define = ( 2284 self._parse_csv( 2285 lambda: self.expression( 2286 exp.Alias, 2287 alias=self._parse_id_var(any_token=True), 2288 this=self._match(TokenType.ALIAS) and self._parse_conjunction(), 2289 ) 2290 ) 2291 if self._match_text_seq("DEFINE") 2292 else None 2293 ) 2294 2295 self._match_r_paren() 2296 2297 return self.expression( 2298 exp.MatchRecognize, 2299 partition_by=partition, 2300 order=order, 2301 measures=measures, 2302 rows=rows, 2303 after=after, 2304 pattern=pattern, 2305 define=define, 2306 alias=self._parse_table_alias(), 2307 ) 2308 2309 def _parse_lateral(self) -> t.Optional[exp.Lateral]: 2310 outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY) 2311 cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY) 2312 2313 if outer_apply or cross_apply: 2314 this = self._parse_select(table=True) 2315 view = None 2316 outer = not cross_apply 2317 elif self._match(TokenType.LATERAL): 2318 this = self._parse_select(table=True) 2319 view = self._match(TokenType.VIEW) 2320 outer = self._match(TokenType.OUTER) 2321 else: 2322 return None 2323 2324 if not this: 2325 this = ( 2326 self._parse_unnest() 2327 or self._parse_function() 2328 or self._parse_id_var(any_token=False) 2329 ) 2330 2331 while self._match(TokenType.DOT): 2332 this = exp.Dot( 2333 this=this, 2334 expression=self._parse_function() or self._parse_id_var(any_token=False), 2335 ) 2336 2337 if view: 2338 table = self._parse_id_var(any_token=False) 2339 columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else [] 2340 table_alias: t.Optional[exp.TableAlias] = self.expression( 2341 exp.TableAlias, this=table, columns=columns 2342 ) 2343 elif isinstance(this, exp.Subquery) and this.alias: 2344 # Ensures parity between the Subquery's and the Lateral's "alias" args 2345 table_alias = this.args["alias"].copy() 2346 else: 2347 table_alias = self._parse_table_alias() 2348 2349 return self.expression(exp.Lateral, this=this, view=view, outer=outer, alias=table_alias) 2350 2351 def _parse_join_parts( 2352 self, 2353 ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]: 2354 return ( 2355 self._match_set(self.JOIN_METHODS) and self._prev, 2356 self._match_set(self.JOIN_SIDES) and self._prev, 2357 self._match_set(self.JOIN_KINDS) and self._prev, 2358 ) 2359 2360 def _parse_join( 2361 self, skip_join_token: bool = False, parse_bracket: bool = False 2362 ) -> t.Optional[exp.Join]: 2363 if self._match(TokenType.COMMA): 2364 return self.expression(exp.Join, this=self._parse_table()) 2365 2366 index = self._index 2367 method, side, kind = self._parse_join_parts() 2368 hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None 2369 join = self._match(TokenType.JOIN) 2370 2371 if not skip_join_token and not join: 2372 self._retreat(index) 2373 kind = None 2374 method = None 2375 side = None 2376 2377 outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False) 2378 cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False) 2379 2380 if not skip_join_token and not join and not outer_apply and not cross_apply: 2381 return None 2382 2383 if outer_apply: 2384 side = Token(TokenType.LEFT, "LEFT") 2385 2386 kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)} 2387 2388 if method: 2389 kwargs["method"] = method.text 2390 if side: 2391 kwargs["side"] = side.text 2392 if kind: 2393 kwargs["kind"] = kind.text 2394 if hint: 2395 kwargs["hint"] = hint 2396 2397 if self._match(TokenType.ON): 2398 kwargs["on"] = self._parse_conjunction() 2399 elif self._match(TokenType.USING): 2400 kwargs["using"] = self._parse_wrapped_id_vars() 2401 elif not (kind and kind.token_type == TokenType.CROSS): 2402 index = self._index 2403 joins = self._parse_joins() 2404 2405 if joins and self._match(TokenType.ON): 2406 kwargs["on"] = self._parse_conjunction() 2407 elif joins and self._match(TokenType.USING): 2408 kwargs["using"] = self._parse_wrapped_id_vars() 2409 else: 2410 joins = None 2411 self._retreat(index) 2412 2413 kwargs["this"].set("joins", joins) 2414 2415 comments = [c for token in (method, side, kind) if token for c in token.comments] 2416 return self.expression(exp.Join, comments=comments, **kwargs) 2417 2418 def _parse_index( 2419 self, 2420 index: t.Optional[exp.Expression] = None, 2421 ) -> t.Optional[exp.Index]: 2422 if index: 2423 unique = None 2424 primary = None 2425 amp = None 2426 2427 self._match(TokenType.ON) 2428 self._match(TokenType.TABLE) # hive 2429 table = self._parse_table_parts(schema=True) 2430 else: 2431 unique = self._match(TokenType.UNIQUE) 2432 primary = self._match_text_seq("PRIMARY") 2433 amp = self._match_text_seq("AMP") 2434 2435 if not self._match(TokenType.INDEX): 2436 return None 2437 2438 index = self._parse_id_var() 2439 table = None 2440 2441 using = self._parse_field() if self._match(TokenType.USING) else None 2442 2443 if self._match(TokenType.L_PAREN, advance=False): 2444 columns = self._parse_wrapped_csv(self._parse_ordered) 2445 else: 2446 columns = None 2447 2448 return self.expression( 2449 exp.Index, 2450 this=index, 2451 table=table, 2452 using=using, 2453 columns=columns, 2454 unique=unique, 2455 primary=primary, 2456 amp=amp, 2457 partition_by=self._parse_partition_by(), 2458 ) 2459 2460 def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]: 2461 hints: t.List[exp.Expression] = [] 2462 if self._match_pair(TokenType.WITH, TokenType.L_PAREN): 2463 # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16 2464 hints.append( 2465 self.expression( 2466 exp.WithTableHint, 2467 expressions=self._parse_csv( 2468 lambda: self._parse_function() or self._parse_var(any_token=True) 2469 ), 2470 ) 2471 ) 2472 self._match_r_paren() 2473 else: 2474 # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html 2475 while self._match_set(self.TABLE_INDEX_HINT_TOKENS): 2476 hint = exp.IndexTableHint(this=self._prev.text.upper()) 2477 2478 self._match_texts({"INDEX", "KEY"}) 2479 if self._match(TokenType.FOR): 2480 hint.set("target", self._advance_any() and self._prev.text.upper()) 2481 2482 hint.set("expressions", self._parse_wrapped_id_vars()) 2483 hints.append(hint) 2484 2485 return hints or None 2486 2487 def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]: 2488 return ( 2489 (not schema and self._parse_function(optional_parens=False)) 2490 or self._parse_id_var(any_token=False) 2491 or self._parse_string_as_identifier() 2492 or self._parse_placeholder() 2493 ) 2494 2495 def _parse_table_parts(self, schema: bool = False) -> exp.Table: 2496 catalog = None 2497 db = None 2498 table = self._parse_table_part(schema=schema) 2499 2500 while self._match(TokenType.DOT): 2501 if catalog: 2502 # This allows nesting the table in arbitrarily many dot expressions if needed 2503 table = self.expression( 2504 exp.Dot, this=table, expression=self._parse_table_part(schema=schema) 2505 ) 2506 else: 2507 catalog = db 2508 db = table 2509 table = self._parse_table_part(schema=schema) 2510 2511 if not table: 2512 self.raise_error(f"Expected table name but got {self._curr}") 2513 2514 return self.expression( 2515 exp.Table, this=table, db=db, catalog=catalog, pivots=self._parse_pivots() 2516 ) 2517 2518 def _parse_table( 2519 self, 2520 schema: bool = False, 2521 joins: bool = False, 2522 alias_tokens: t.Optional[t.Collection[TokenType]] = None, 2523 parse_bracket: bool = False, 2524 ) -> t.Optional[exp.Expression]: 2525 lateral = self._parse_lateral() 2526 if lateral: 2527 return lateral 2528 2529 unnest = self._parse_unnest() 2530 if unnest: 2531 return unnest 2532 2533 values = self._parse_derived_table_values() 2534 if values: 2535 return values 2536 2537 subquery = self._parse_select(table=True) 2538 if subquery: 2539 if not subquery.args.get("pivots"): 2540 subquery.set("pivots", self._parse_pivots()) 2541 return subquery 2542 2543 bracket = parse_bracket and self._parse_bracket(None) 2544 bracket = self.expression(exp.Table, this=bracket) if bracket else None 2545 this: exp.Expression = bracket or self._parse_table_parts(schema=schema) 2546 2547 if schema: 2548 return self._parse_schema(this=this) 2549 2550 version = self._parse_version() 2551 2552 if version: 2553 this.set("version", version) 2554 2555 if self.ALIAS_POST_TABLESAMPLE: 2556 table_sample = self._parse_table_sample() 2557 2558 alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS) 2559 if alias: 2560 this.set("alias", alias) 2561 2562 this.set("hints", self._parse_table_hints()) 2563 2564 if not this.args.get("pivots"): 2565 this.set("pivots", self._parse_pivots()) 2566 2567 if not self.ALIAS_POST_TABLESAMPLE: 2568 table_sample = self._parse_table_sample() 2569 2570 if table_sample: 2571 table_sample.set("this", this) 2572 this = table_sample 2573 2574 if joins: 2575 for join in iter(self._parse_join, None): 2576 this.append("joins", join) 2577 2578 return this 2579 2580 def _parse_version(self) -> t.Optional[exp.Version]: 2581 if self._match(TokenType.TIMESTAMP_SNAPSHOT): 2582 this = "TIMESTAMP" 2583 elif self._match(TokenType.VERSION_SNAPSHOT): 2584 this = "VERSION" 2585 else: 2586 return None 2587 2588 if self._match_set((TokenType.FROM, TokenType.BETWEEN)): 2589 kind = self._prev.text.upper() 2590 start = self._parse_bitwise() 2591 self._match_texts(("TO", "AND")) 2592 end = self._parse_bitwise() 2593 expression: t.Optional[exp.Expression] = self.expression( 2594 exp.Tuple, expressions=[start, end] 2595 ) 2596 elif self._match_text_seq("CONTAINED", "IN"): 2597 kind = "CONTAINED IN" 2598 expression = self.expression( 2599 exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise) 2600 ) 2601 elif self._match(TokenType.ALL): 2602 kind = "ALL" 2603 expression = None 2604 else: 2605 self._match_text_seq("AS", "OF") 2606 kind = "AS OF" 2607 expression = self._parse_type() 2608 2609 return self.expression(exp.Version, this=this, expression=expression, kind=kind) 2610 2611 def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]: 2612 if not self._match(TokenType.UNNEST): 2613 return None 2614 2615 expressions = self._parse_wrapped_csv(self._parse_type) 2616 ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY) 2617 2618 alias = self._parse_table_alias() if with_alias else None 2619 2620 if alias and self.UNNEST_COLUMN_ONLY: 2621 if alias.args.get("columns"): 2622 self.raise_error("Unexpected extra column alias in unnest.") 2623 2624 alias.set("columns", [alias.this]) 2625 alias.set("this", None) 2626 2627 offset = None 2628 if self._match_pair(TokenType.WITH, TokenType.OFFSET): 2629 self._match(TokenType.ALIAS) 2630 offset = self._parse_id_var() or exp.to_identifier("offset") 2631 2632 return self.expression( 2633 exp.Unnest, expressions=expressions, ordinality=ordinality, alias=alias, offset=offset 2634 ) 2635 2636 def _parse_derived_table_values(self) -> t.Optional[exp.Values]: 2637 is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES) 2638 if not is_derived and not self._match(TokenType.VALUES): 2639 return None 2640 2641 expressions = self._parse_csv(self._parse_value) 2642 alias = self._parse_table_alias() 2643 2644 if is_derived: 2645 self._match_r_paren() 2646 2647 return self.expression( 2648 exp.Values, expressions=expressions, alias=alias or self._parse_table_alias() 2649 ) 2650 2651 def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]: 2652 if not self._match(TokenType.TABLE_SAMPLE) and not ( 2653 as_modifier and self._match_text_seq("USING", "SAMPLE") 2654 ): 2655 return None 2656 2657 bucket_numerator = None 2658 bucket_denominator = None 2659 bucket_field = None 2660 percent = None 2661 rows = None 2662 size = None 2663 seed = None 2664 2665 kind = ( 2666 self._prev.text if self._prev.token_type == TokenType.TABLE_SAMPLE else "USING SAMPLE" 2667 ) 2668 method = self._parse_var(tokens=(TokenType.ROW,)) 2669 2670 self._match(TokenType.L_PAREN) 2671 2672 num = self._parse_number() 2673 2674 if self._match_text_seq("BUCKET"): 2675 bucket_numerator = self._parse_number() 2676 self._match_text_seq("OUT", "OF") 2677 bucket_denominator = bucket_denominator = self._parse_number() 2678 self._match(TokenType.ON) 2679 bucket_field = self._parse_field() 2680 elif self._match_set((TokenType.PERCENT, TokenType.MOD)): 2681 percent = num 2682 elif self._match(TokenType.ROWS): 2683 rows = num 2684 else: 2685 size = num 2686 2687 self._match(TokenType.R_PAREN) 2688 2689 if self._match(TokenType.L_PAREN): 2690 method = self._parse_var() 2691 seed = self._match(TokenType.COMMA) and self._parse_number() 2692 self._match_r_paren() 2693 elif self._match_texts(("SEED", "REPEATABLE")): 2694 seed = self._parse_wrapped(self._parse_number) 2695 2696 return self.expression( 2697 exp.TableSample, 2698 method=method, 2699 bucket_numerator=bucket_numerator, 2700 bucket_denominator=bucket_denominator, 2701 bucket_field=bucket_field, 2702 percent=percent, 2703 rows=rows, 2704 size=size, 2705 seed=seed, 2706 kind=kind, 2707 ) 2708 2709 def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]: 2710 return list(iter(self._parse_pivot, None)) or None 2711 2712 def _parse_joins(self) -> t.Optional[t.List[exp.Join]]: 2713 return list(iter(self._parse_join, None)) or None 2714 2715 # https://duckdb.org/docs/sql/statements/pivot 2716 def _parse_simplified_pivot(self) -> exp.Pivot: 2717 def _parse_on() -> t.Optional[exp.Expression]: 2718 this = self._parse_bitwise() 2719 return self._parse_in(this) if self._match(TokenType.IN) else this 2720 2721 this = self._parse_table() 2722 expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on) 2723 using = self._match(TokenType.USING) and self._parse_csv( 2724 lambda: self._parse_alias(self._parse_function()) 2725 ) 2726 group = self._parse_group() 2727 return self.expression( 2728 exp.Pivot, this=this, expressions=expressions, using=using, group=group 2729 ) 2730 2731 def _parse_pivot(self) -> t.Optional[exp.Pivot]: 2732 index = self._index 2733 include_nulls = None 2734 2735 if self._match(TokenType.PIVOT): 2736 unpivot = False 2737 elif self._match(TokenType.UNPIVOT): 2738 unpivot = True 2739 2740 # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax 2741 if self._match_text_seq("INCLUDE", "NULLS"): 2742 include_nulls = True 2743 elif self._match_text_seq("EXCLUDE", "NULLS"): 2744 include_nulls = False 2745 else: 2746 return None 2747 2748 expressions = [] 2749 field = None 2750 2751 if not self._match(TokenType.L_PAREN): 2752 self._retreat(index) 2753 return None 2754 2755 if unpivot: 2756 expressions = self._parse_csv(self._parse_column) 2757 else: 2758 expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function())) 2759 2760 if not expressions: 2761 self.raise_error("Failed to parse PIVOT's aggregation list") 2762 2763 if not self._match(TokenType.FOR): 2764 self.raise_error("Expecting FOR") 2765 2766 value = self._parse_column() 2767 2768 if not self._match(TokenType.IN): 2769 self.raise_error("Expecting IN") 2770 2771 field = self._parse_in(value, alias=True) 2772 2773 self._match_r_paren() 2774 2775 pivot = self.expression( 2776 exp.Pivot, 2777 expressions=expressions, 2778 field=field, 2779 unpivot=unpivot, 2780 include_nulls=include_nulls, 2781 ) 2782 2783 if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False): 2784 pivot.set("alias", self._parse_table_alias()) 2785 2786 if not unpivot: 2787 names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions)) 2788 2789 columns: t.List[exp.Expression] = [] 2790 for fld in pivot.args["field"].expressions: 2791 field_name = fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name 2792 for name in names: 2793 if self.PREFIXED_PIVOT_COLUMNS: 2794 name = f"{name}_{field_name}" if name else field_name 2795 else: 2796 name = f"{field_name}_{name}" if name else field_name 2797 2798 columns.append(exp.to_identifier(name)) 2799 2800 pivot.set("columns", columns) 2801 2802 return pivot 2803 2804 def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]: 2805 return [agg.alias for agg in aggregations] 2806 2807 def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]: 2808 if not skip_where_token and not self._match(TokenType.WHERE): 2809 return None 2810 2811 return self.expression( 2812 exp.Where, comments=self._prev_comments, this=self._parse_conjunction() 2813 ) 2814 2815 def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]: 2816 if not skip_group_by_token and not self._match(TokenType.GROUP_BY): 2817 return None 2818 2819 elements = defaultdict(list) 2820 2821 if self._match(TokenType.ALL): 2822 return self.expression(exp.Group, all=True) 2823 2824 while True: 2825 expressions = self._parse_csv(self._parse_conjunction) 2826 if expressions: 2827 elements["expressions"].extend(expressions) 2828 2829 grouping_sets = self._parse_grouping_sets() 2830 if grouping_sets: 2831 elements["grouping_sets"].extend(grouping_sets) 2832 2833 rollup = None 2834 cube = None 2835 totals = None 2836 2837 with_ = self._match(TokenType.WITH) 2838 if self._match(TokenType.ROLLUP): 2839 rollup = with_ or self._parse_wrapped_csv(self._parse_column) 2840 elements["rollup"].extend(ensure_list(rollup)) 2841 2842 if self._match(TokenType.CUBE): 2843 cube = with_ or self._parse_wrapped_csv(self._parse_column) 2844 elements["cube"].extend(ensure_list(cube)) 2845 2846 if self._match_text_seq("TOTALS"): 2847 totals = True 2848 elements["totals"] = True # type: ignore 2849 2850 if not (grouping_sets or rollup or cube or totals): 2851 break 2852 2853 return self.expression(exp.Group, **elements) # type: ignore 2854 2855 def _parse_grouping_sets(self) -> t.Optional[t.List[exp.Expression]]: 2856 if not self._match(TokenType.GROUPING_SETS): 2857 return None 2858 2859 return self._parse_wrapped_csv(self._parse_grouping_set) 2860 2861 def _parse_grouping_set(self) -> t.Optional[exp.Expression]: 2862 if self._match(TokenType.L_PAREN): 2863 grouping_set = self._parse_csv(self._parse_column) 2864 self._match_r_paren() 2865 return self.expression(exp.Tuple, expressions=grouping_set) 2866 2867 return self._parse_column() 2868 2869 def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]: 2870 if not skip_having_token and not self._match(TokenType.HAVING): 2871 return None 2872 return self.expression(exp.Having, this=self._parse_conjunction()) 2873 2874 def _parse_qualify(self) -> t.Optional[exp.Qualify]: 2875 if not self._match(TokenType.QUALIFY): 2876 return None 2877 return self.expression(exp.Qualify, this=self._parse_conjunction()) 2878 2879 def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]: 2880 if skip_start_token: 2881 start = None 2882 elif self._match(TokenType.START_WITH): 2883 start = self._parse_conjunction() 2884 else: 2885 return None 2886 2887 self._match(TokenType.CONNECT_BY) 2888 self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression( 2889 exp.Prior, this=self._parse_bitwise() 2890 ) 2891 connect = self._parse_conjunction() 2892 self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR") 2893 return self.expression(exp.Connect, start=start, connect=connect) 2894 2895 def _parse_order( 2896 self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False 2897 ) -> t.Optional[exp.Expression]: 2898 if not skip_order_token and not self._match(TokenType.ORDER_BY): 2899 return this 2900 2901 return self.expression( 2902 exp.Order, this=this, expressions=self._parse_csv(self._parse_ordered) 2903 ) 2904 2905 def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]: 2906 if not self._match(token): 2907 return None 2908 return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered)) 2909 2910 def _parse_ordered(self) -> exp.Ordered: 2911 this = self._parse_conjunction() 2912 self._match(TokenType.ASC) 2913 2914 is_desc = self._match(TokenType.DESC) 2915 is_nulls_first = self._match_text_seq("NULLS", "FIRST") 2916 is_nulls_last = self._match_text_seq("NULLS", "LAST") 2917 desc = is_desc or False 2918 asc = not desc 2919 nulls_first = is_nulls_first or False 2920 explicitly_null_ordered = is_nulls_first or is_nulls_last 2921 2922 if ( 2923 not explicitly_null_ordered 2924 and ( 2925 (asc and self.NULL_ORDERING == "nulls_are_small") 2926 or (desc and self.NULL_ORDERING != "nulls_are_small") 2927 ) 2928 and self.NULL_ORDERING != "nulls_are_last" 2929 ): 2930 nulls_first = True 2931 2932 return self.expression(exp.Ordered, this=this, desc=desc, nulls_first=nulls_first) 2933 2934 def _parse_limit( 2935 self, this: t.Optional[exp.Expression] = None, top: bool = False 2936 ) -> t.Optional[exp.Expression]: 2937 if self._match(TokenType.TOP if top else TokenType.LIMIT): 2938 comments = self._prev_comments 2939 if top: 2940 limit_paren = self._match(TokenType.L_PAREN) 2941 expression = self._parse_number() 2942 2943 if limit_paren: 2944 self._match_r_paren() 2945 else: 2946 expression = self._parse_term() 2947 2948 if self._match(TokenType.COMMA): 2949 offset = expression 2950 expression = self._parse_term() 2951 else: 2952 offset = None 2953 2954 limit_exp = self.expression( 2955 exp.Limit, this=this, expression=expression, offset=offset, comments=comments 2956 ) 2957 2958 return limit_exp 2959 2960 if self._match(TokenType.FETCH): 2961 direction = self._match_set((TokenType.FIRST, TokenType.NEXT)) 2962 direction = self._prev.text if direction else "FIRST" 2963 2964 count = self._parse_number() 2965 percent = self._match(TokenType.PERCENT) 2966 2967 self._match_set((TokenType.ROW, TokenType.ROWS)) 2968 2969 only = self._match_text_seq("ONLY") 2970 with_ties = self._match_text_seq("WITH", "TIES") 2971 2972 if only and with_ties: 2973 self.raise_error("Cannot specify both ONLY and WITH TIES in FETCH clause") 2974 2975 return self.expression( 2976 exp.Fetch, 2977 direction=direction, 2978 count=count, 2979 percent=percent, 2980 with_ties=with_ties, 2981 ) 2982 2983 return this 2984 2985 def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]: 2986 if not self._match(TokenType.OFFSET): 2987 return this 2988 2989 count = self._parse_term() 2990 self._match_set((TokenType.ROW, TokenType.ROWS)) 2991 return self.expression(exp.Offset, this=this, expression=count) 2992 2993 def _parse_locks(self) -> t.List[exp.Lock]: 2994 locks = [] 2995 while True: 2996 if self._match_text_seq("FOR", "UPDATE"): 2997 update = True 2998 elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq( 2999 "LOCK", "IN", "SHARE", "MODE" 3000 ): 3001 update = False 3002 else: 3003 break 3004 3005 expressions = None 3006 if self._match_text_seq("OF"): 3007 expressions = self._parse_csv(lambda: self._parse_table(schema=True)) 3008 3009 wait: t.Optional[bool | exp.Expression] = None 3010 if self._match_text_seq("NOWAIT"): 3011 wait = True 3012 elif self._match_text_seq("WAIT"): 3013 wait = self._parse_primary() 3014 elif self._match_text_seq("SKIP", "LOCKED"): 3015 wait = False 3016 3017 locks.append( 3018 self.expression(exp.Lock, update=update, expressions=expressions, wait=wait) 3019 ) 3020 3021 return locks 3022 3023 def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3024 if not self._match_set(self.SET_OPERATIONS): 3025 return this 3026 3027 token_type = self._prev.token_type 3028 3029 if token_type == TokenType.UNION: 3030 expression = exp.Union 3031 elif token_type == TokenType.EXCEPT: 3032 expression = exp.Except 3033 else: 3034 expression = exp.Intersect 3035 3036 return self.expression( 3037 expression, 3038 this=this, 3039 distinct=self._match(TokenType.DISTINCT) or not self._match(TokenType.ALL), 3040 by_name=self._match_text_seq("BY", "NAME"), 3041 expression=self._parse_set_operations(self._parse_select(nested=True)), 3042 ) 3043 3044 def _parse_expression(self) -> t.Optional[exp.Expression]: 3045 return self._parse_alias(self._parse_conjunction()) 3046 3047 def _parse_conjunction(self) -> t.Optional[exp.Expression]: 3048 return self._parse_tokens(self._parse_equality, self.CONJUNCTION) 3049 3050 def _parse_equality(self) -> t.Optional[exp.Expression]: 3051 return self._parse_tokens(self._parse_comparison, self.EQUALITY) 3052 3053 def _parse_comparison(self) -> t.Optional[exp.Expression]: 3054 return self._parse_tokens(self._parse_range, self.COMPARISON) 3055 3056 def _parse_range(self) -> t.Optional[exp.Expression]: 3057 this = self._parse_bitwise() 3058 negate = self._match(TokenType.NOT) 3059 3060 if self._match_set(self.RANGE_PARSERS): 3061 expression = self.RANGE_PARSERS[self._prev.token_type](self, this) 3062 if not expression: 3063 return this 3064 3065 this = expression 3066 elif self._match(TokenType.ISNULL): 3067 this = self.expression(exp.Is, this=this, expression=exp.Null()) 3068 3069 # Postgres supports ISNULL and NOTNULL for conditions. 3070 # https://blog.andreiavram.ro/postgresql-null-composite-type/ 3071 if self._match(TokenType.NOTNULL): 3072 this = self.expression(exp.Is, this=this, expression=exp.Null()) 3073 this = self.expression(exp.Not, this=this) 3074 3075 if negate: 3076 this = self.expression(exp.Not, this=this) 3077 3078 if self._match(TokenType.IS): 3079 this = self._parse_is(this) 3080 3081 return this 3082 3083 def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3084 index = self._index - 1 3085 negate = self._match(TokenType.NOT) 3086 3087 if self._match_text_seq("DISTINCT", "FROM"): 3088 klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ 3089 return self.expression(klass, this=this, expression=self._parse_expression()) 3090 3091 expression = self._parse_null() or self._parse_boolean() 3092 if not expression: 3093 self._retreat(index) 3094 return None 3095 3096 this = self.expression(exp.Is, this=this, expression=expression) 3097 return self.expression(exp.Not, this=this) if negate else this 3098 3099 def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In: 3100 unnest = self._parse_unnest(with_alias=False) 3101 if unnest: 3102 this = self.expression(exp.In, this=this, unnest=unnest) 3103 elif self._match(TokenType.L_PAREN): 3104 expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias)) 3105 3106 if len(expressions) == 1 and isinstance(expressions[0], exp.Subqueryable): 3107 this = self.expression(exp.In, this=this, query=expressions[0]) 3108 else: 3109 this = self.expression(exp.In, this=this, expressions=expressions) 3110 3111 self._match_r_paren(this) 3112 else: 3113 this = self.expression(exp.In, this=this, field=self._parse_field()) 3114 3115 return this 3116 3117 def _parse_between(self, this: exp.Expression) -> exp.Between: 3118 low = self._parse_bitwise() 3119 self._match(TokenType.AND) 3120 high = self._parse_bitwise() 3121 return self.expression(exp.Between, this=this, low=low, high=high) 3122 3123 def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3124 if not self._match(TokenType.ESCAPE): 3125 return this 3126 return self.expression(exp.Escape, this=this, expression=self._parse_string()) 3127 3128 def _parse_interval(self) -> t.Optional[exp.Interval]: 3129 index = self._index 3130 3131 if not self._match(TokenType.INTERVAL): 3132 return None 3133 3134 if self._match(TokenType.STRING, advance=False): 3135 this = self._parse_primary() 3136 else: 3137 this = self._parse_term() 3138 3139 if not this: 3140 self._retreat(index) 3141 return None 3142 3143 unit = self._parse_function() or self._parse_var(any_token=True) 3144 3145 # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse 3146 # each INTERVAL expression into this canonical form so it's easy to transpile 3147 if this and this.is_number: 3148 this = exp.Literal.string(this.name) 3149 elif this and this.is_string: 3150 parts = this.name.split() 3151 3152 if len(parts) == 2: 3153 if unit: 3154 # this is not actually a unit, it's something else 3155 unit = None 3156 self._retreat(self._index - 1) 3157 else: 3158 this = exp.Literal.string(parts[0]) 3159 unit = self.expression(exp.Var, this=parts[1]) 3160 3161 return self.expression(exp.Interval, this=this, unit=unit) 3162 3163 def _parse_bitwise(self) -> t.Optional[exp.Expression]: 3164 this = self._parse_term() 3165 3166 while True: 3167 if self._match_set(self.BITWISE): 3168 this = self.expression( 3169 self.BITWISE[self._prev.token_type], 3170 this=this, 3171 expression=self._parse_term(), 3172 ) 3173 elif self._match(TokenType.DQMARK): 3174 this = self.expression(exp.Coalesce, this=this, expressions=self._parse_term()) 3175 elif self._match_pair(TokenType.LT, TokenType.LT): 3176 this = self.expression( 3177 exp.BitwiseLeftShift, this=this, expression=self._parse_term() 3178 ) 3179 elif self._match_pair(TokenType.GT, TokenType.GT): 3180 this = self.expression( 3181 exp.BitwiseRightShift, this=this, expression=self._parse_term() 3182 ) 3183 else: 3184 break 3185 3186 return this 3187 3188 def _parse_term(self) -> t.Optional[exp.Expression]: 3189 return self._parse_tokens(self._parse_factor, self.TERM) 3190 3191 def _parse_factor(self) -> t.Optional[exp.Expression]: 3192 return self._parse_tokens(self._parse_unary, self.FACTOR) 3193 3194 def _parse_unary(self) -> t.Optional[exp.Expression]: 3195 if self._match_set(self.UNARY_PARSERS): 3196 return self.UNARY_PARSERS[self._prev.token_type](self) 3197 return self._parse_at_time_zone(self._parse_type()) 3198 3199 def _parse_type(self) -> t.Optional[exp.Expression]: 3200 interval = self._parse_interval() 3201 if interval: 3202 return interval 3203 3204 index = self._index 3205 data_type = self._parse_types(check_func=True, allow_identifiers=False) 3206 this = self._parse_column() 3207 3208 if data_type: 3209 if isinstance(this, exp.Literal): 3210 parser = self.TYPE_LITERAL_PARSERS.get(data_type.this) 3211 if parser: 3212 return parser(self, this, data_type) 3213 return self.expression(exp.Cast, this=this, to=data_type) 3214 if not data_type.expressions: 3215 self._retreat(index) 3216 return self._parse_column() 3217 return self._parse_column_ops(data_type) 3218 3219 return this 3220 3221 def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]: 3222 this = self._parse_type() 3223 if not this: 3224 return None 3225 3226 return self.expression( 3227 exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True) 3228 ) 3229 3230 def _parse_types( 3231 self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True 3232 ) -> t.Optional[exp.Expression]: 3233 index = self._index 3234 3235 prefix = self._match_text_seq("SYSUDTLIB", ".") 3236 3237 if not self._match_set(self.TYPE_TOKENS): 3238 identifier = allow_identifiers and self._parse_id_var( 3239 any_token=False, tokens=(TokenType.VAR,) 3240 ) 3241 3242 if identifier: 3243 tokens = self._tokenizer.tokenize(identifier.name) 3244 3245 if len(tokens) != 1: 3246 self.raise_error("Unexpected identifier", self._prev) 3247 3248 if tokens[0].token_type in self.TYPE_TOKENS: 3249 self._prev = tokens[0] 3250 elif self.SUPPORTS_USER_DEFINED_TYPES: 3251 return identifier 3252 else: 3253 return None 3254 else: 3255 return None 3256 3257 type_token = self._prev.token_type 3258 3259 if type_token == TokenType.PSEUDO_TYPE: 3260 return self.expression(exp.PseudoType, this=self._prev.text) 3261 3262 nested = type_token in self.NESTED_TYPE_TOKENS 3263 is_struct = type_token in self.STRUCT_TYPE_TOKENS 3264 expressions = None 3265 maybe_func = False 3266 3267 if self._match(TokenType.L_PAREN): 3268 if is_struct: 3269 expressions = self._parse_csv(self._parse_struct_types) 3270 elif nested: 3271 expressions = self._parse_csv( 3272 lambda: self._parse_types( 3273 check_func=check_func, schema=schema, allow_identifiers=allow_identifiers 3274 ) 3275 ) 3276 elif type_token in self.ENUM_TYPE_TOKENS: 3277 expressions = self._parse_csv(self._parse_equality) 3278 else: 3279 expressions = self._parse_csv(self._parse_type_size) 3280 3281 if not expressions or not self._match(TokenType.R_PAREN): 3282 self._retreat(index) 3283 return None 3284 3285 maybe_func = True 3286 3287 this: t.Optional[exp.Expression] = None 3288 values: t.Optional[t.List[exp.Expression]] = None 3289 3290 if nested and self._match(TokenType.LT): 3291 if is_struct: 3292 expressions = self._parse_csv(self._parse_struct_types) 3293 else: 3294 expressions = self._parse_csv( 3295 lambda: self._parse_types( 3296 check_func=check_func, schema=schema, allow_identifiers=allow_identifiers 3297 ) 3298 ) 3299 3300 if not self._match(TokenType.GT): 3301 self.raise_error("Expecting >") 3302 3303 if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)): 3304 values = self._parse_csv(self._parse_conjunction) 3305 self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN)) 3306 3307 if type_token in self.TIMESTAMPS: 3308 if self._match_text_seq("WITH", "TIME", "ZONE"): 3309 maybe_func = False 3310 tz_type = ( 3311 exp.DataType.Type.TIMETZ 3312 if type_token in self.TIMES 3313 else exp.DataType.Type.TIMESTAMPTZ 3314 ) 3315 this = exp.DataType(this=tz_type, expressions=expressions) 3316 elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"): 3317 maybe_func = False 3318 this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions) 3319 elif self._match_text_seq("WITHOUT", "TIME", "ZONE"): 3320 maybe_func = False 3321 elif type_token == TokenType.INTERVAL: 3322 if self._match_text_seq("YEAR", "TO", "MONTH"): 3323 span: t.Optional[t.List[exp.Expression]] = [exp.IntervalYearToMonthSpan()] 3324 elif self._match_text_seq("DAY", "TO", "SECOND"): 3325 span = [exp.IntervalDayToSecondSpan()] 3326 else: 3327 span = None 3328 3329 unit = not span and self._parse_var() 3330 if not unit: 3331 this = self.expression( 3332 exp.DataType, this=exp.DataType.Type.INTERVAL, expressions=span 3333 ) 3334 else: 3335 this = self.expression(exp.Interval, unit=unit) 3336 3337 if maybe_func and check_func: 3338 index2 = self._index 3339 peek = self._parse_string() 3340 3341 if not peek: 3342 self._retreat(index) 3343 return None 3344 3345 self._retreat(index2) 3346 3347 if not this: 3348 this = exp.DataType( 3349 this=exp.DataType.Type[type_token.value], 3350 expressions=expressions, 3351 nested=nested, 3352 values=values, 3353 prefix=prefix, 3354 ) 3355 3356 while self._match_pair(TokenType.L_BRACKET, TokenType.R_BRACKET): 3357 this = exp.DataType(this=exp.DataType.Type.ARRAY, expressions=[this], nested=True) 3358 3359 return this 3360 3361 def _parse_struct_types(self) -> t.Optional[exp.Expression]: 3362 this = self._parse_type() or self._parse_id_var() 3363 self._match(TokenType.COLON) 3364 return self._parse_column_def(this) 3365 3366 def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3367 if not self._match_text_seq("AT", "TIME", "ZONE"): 3368 return this 3369 return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary()) 3370 3371 def _parse_column(self) -> t.Optional[exp.Expression]: 3372 this = self._parse_field() 3373 if isinstance(this, exp.Identifier): 3374 this = self.expression(exp.Column, this=this) 3375 elif not this: 3376 return self._parse_bracket(this) 3377 return self._parse_column_ops(this) 3378 3379 def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3380 this = self._parse_bracket(this) 3381 3382 while self._match_set(self.COLUMN_OPERATORS): 3383 op_token = self._prev.token_type 3384 op = self.COLUMN_OPERATORS.get(op_token) 3385 3386 if op_token == TokenType.DCOLON: 3387 field = self._parse_types() 3388 if not field: 3389 self.raise_error("Expected type") 3390 elif op and self._curr: 3391 self._advance() 3392 value = self._prev.text 3393 field = ( 3394 exp.Literal.number(value) 3395 if self._prev.token_type == TokenType.NUMBER 3396 else exp.Literal.string(value) 3397 ) 3398 else: 3399 field = self._parse_field(anonymous_func=True, any_token=True) 3400 3401 if isinstance(field, exp.Func): 3402 # bigquery allows function calls like x.y.count(...) 3403 # SAFE.SUBSTR(...) 3404 # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules 3405 this = self._replace_columns_with_dots(this) 3406 3407 if op: 3408 this = op(self, this, field) 3409 elif isinstance(this, exp.Column) and not this.args.get("catalog"): 3410 this = self.expression( 3411 exp.Column, 3412 this=field, 3413 table=this.this, 3414 db=this.args.get("table"), 3415 catalog=this.args.get("db"), 3416 ) 3417 else: 3418 this = self.expression(exp.Dot, this=this, expression=field) 3419 this = self._parse_bracket(this) 3420 return this 3421 3422 def _parse_primary(self) -> t.Optional[exp.Expression]: 3423 if self._match_set(self.PRIMARY_PARSERS): 3424 token_type = self._prev.token_type 3425 primary = self.PRIMARY_PARSERS[token_type](self, self._prev) 3426 3427 if token_type == TokenType.STRING: 3428 expressions = [primary] 3429 while self._match(TokenType.STRING): 3430 expressions.append(exp.Literal.string(self._prev.text)) 3431 3432 if len(expressions) > 1: 3433 return self.expression(exp.Concat, expressions=expressions) 3434 3435 return primary 3436 3437 if self._match_pair(TokenType.DOT, TokenType.NUMBER): 3438 return exp.Literal.number(f"0.{self._prev.text}") 3439 3440 if self._match(TokenType.L_PAREN): 3441 comments = self._prev_comments 3442 query = self._parse_select() 3443 3444 if query: 3445 expressions = [query] 3446 else: 3447 expressions = self._parse_expressions() 3448 3449 this = self._parse_query_modifiers(seq_get(expressions, 0)) 3450 3451 if isinstance(this, exp.Subqueryable): 3452 this = self._parse_set_operations( 3453 self._parse_subquery(this=this, parse_alias=False) 3454 ) 3455 elif len(expressions) > 1: 3456 this = self.expression(exp.Tuple, expressions=expressions) 3457 else: 3458 this = self.expression(exp.Paren, this=self._parse_set_operations(this)) 3459 3460 if this: 3461 this.add_comments(comments) 3462 3463 self._match_r_paren(expression=this) 3464 return this 3465 3466 return None 3467 3468 def _parse_field( 3469 self, 3470 any_token: bool = False, 3471 tokens: t.Optional[t.Collection[TokenType]] = None, 3472 anonymous_func: bool = False, 3473 ) -> t.Optional[exp.Expression]: 3474 return ( 3475 self._parse_primary() 3476 or self._parse_function(anonymous=anonymous_func) 3477 or self._parse_id_var(any_token=any_token, tokens=tokens) 3478 ) 3479 3480 def _parse_function( 3481 self, 3482 functions: t.Optional[t.Dict[str, t.Callable]] = None, 3483 anonymous: bool = False, 3484 optional_parens: bool = True, 3485 ) -> t.Optional[exp.Expression]: 3486 if not self._curr: 3487 return None 3488 3489 token_type = self._curr.token_type 3490 this = self._curr.text 3491 upper = this.upper() 3492 3493 parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper) 3494 if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS: 3495 self._advance() 3496 return parser(self) 3497 3498 if not self._next or self._next.token_type != TokenType.L_PAREN: 3499 if optional_parens and token_type in self.NO_PAREN_FUNCTIONS: 3500 self._advance() 3501 return self.expression(self.NO_PAREN_FUNCTIONS[token_type]) 3502 3503 return None 3504 3505 if token_type not in self.FUNC_TOKENS: 3506 return None 3507 3508 self._advance(2) 3509 3510 parser = self.FUNCTION_PARSERS.get(upper) 3511 if parser and not anonymous: 3512 this = parser(self) 3513 else: 3514 subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type) 3515 3516 if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH): 3517 this = self.expression(subquery_predicate, this=self._parse_select()) 3518 self._match_r_paren() 3519 return this 3520 3521 if functions is None: 3522 functions = self.FUNCTIONS 3523 3524 function = functions.get(upper) 3525 3526 alias = upper in self.FUNCTIONS_WITH_ALIASED_ARGS 3527 args = self._parse_csv(lambda: self._parse_lambda(alias=alias)) 3528 3529 if function and not anonymous: 3530 func = self.validate_expression(function(args), args) 3531 if not self.NORMALIZE_FUNCTIONS: 3532 func.meta["name"] = this 3533 this = func 3534 else: 3535 this = self.expression(exp.Anonymous, this=this, expressions=args) 3536 3537 self._match_r_paren(this) 3538 return self._parse_window(this) 3539 3540 def _parse_function_parameter(self) -> t.Optional[exp.Expression]: 3541 return self._parse_column_def(self._parse_id_var()) 3542 3543 def _parse_user_defined_function( 3544 self, kind: t.Optional[TokenType] = None 3545 ) -> t.Optional[exp.Expression]: 3546 this = self._parse_id_var() 3547 3548 while self._match(TokenType.DOT): 3549 this = self.expression(exp.Dot, this=this, expression=self._parse_id_var()) 3550 3551 if not self._match(TokenType.L_PAREN): 3552 return this 3553 3554 expressions = self._parse_csv(self._parse_function_parameter) 3555 self._match_r_paren() 3556 return self.expression( 3557 exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True 3558 ) 3559 3560 def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier: 3561 literal = self._parse_primary() 3562 if literal: 3563 return self.expression(exp.Introducer, this=token.text, expression=literal) 3564 3565 return self.expression(exp.Identifier, this=token.text) 3566 3567 def _parse_session_parameter(self) -> exp.SessionParameter: 3568 kind = None 3569 this = self._parse_id_var() or self._parse_primary() 3570 3571 if this and self._match(TokenType.DOT): 3572 kind = this.name 3573 this = self._parse_var() or self._parse_primary() 3574 3575 return self.expression(exp.SessionParameter, this=this, kind=kind) 3576 3577 def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]: 3578 index = self._index 3579 3580 if self._match(TokenType.L_PAREN): 3581 expressions = t.cast( 3582 t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_id_var) 3583 ) 3584 3585 if not self._match(TokenType.R_PAREN): 3586 self._retreat(index) 3587 else: 3588 expressions = [self._parse_id_var()] 3589 3590 if self._match_set(self.LAMBDAS): 3591 return self.LAMBDAS[self._prev.token_type](self, expressions) 3592 3593 self._retreat(index) 3594 3595 this: t.Optional[exp.Expression] 3596 3597 if self._match(TokenType.DISTINCT): 3598 this = self.expression( 3599 exp.Distinct, expressions=self._parse_csv(self._parse_conjunction) 3600 ) 3601 else: 3602 this = self._parse_select_or_expression(alias=alias) 3603 3604 return self._parse_limit(self._parse_order(self._parse_respect_or_ignore_nulls(this))) 3605 3606 def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]: 3607 index = self._index 3608 3609 if not self.errors: 3610 try: 3611 if self._parse_select(nested=True): 3612 return this 3613 except ParseError: 3614 pass 3615 finally: 3616 self.errors.clear() 3617 self._retreat(index) 3618 3619 if not self._match(TokenType.L_PAREN): 3620 return this 3621 3622 args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def()) 3623 3624 self._match_r_paren() 3625 return self.expression(exp.Schema, this=this, expressions=args) 3626 3627 def _parse_field_def(self) -> t.Optional[exp.Expression]: 3628 return self._parse_column_def(self._parse_field(any_token=True)) 3629 3630 def _parse_column_def(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3631 # column defs are not really columns, they're identifiers 3632 if isinstance(this, exp.Column): 3633 this = this.this 3634 3635 kind = self._parse_types(schema=True) 3636 3637 if self._match_text_seq("FOR", "ORDINALITY"): 3638 return self.expression(exp.ColumnDef, this=this, ordinality=True) 3639 3640 constraints: t.List[exp.Expression] = [] 3641 3642 if not kind and self._match(TokenType.ALIAS): 3643 constraints.append( 3644 self.expression( 3645 exp.ComputedColumnConstraint, 3646 this=self._parse_conjunction(), 3647 persisted=self._match_text_seq("PERSISTED"), 3648 not_null=self._match_pair(TokenType.NOT, TokenType.NULL), 3649 ) 3650 ) 3651 3652 while True: 3653 constraint = self._parse_column_constraint() 3654 if not constraint: 3655 break 3656 constraints.append(constraint) 3657 3658 if not kind and not constraints: 3659 return this 3660 3661 return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints) 3662 3663 def _parse_auto_increment( 3664 self, 3665 ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint: 3666 start = None 3667 increment = None 3668 3669 if self._match(TokenType.L_PAREN, advance=False): 3670 args = self._parse_wrapped_csv(self._parse_bitwise) 3671 start = seq_get(args, 0) 3672 increment = seq_get(args, 1) 3673 elif self._match_text_seq("START"): 3674 start = self._parse_bitwise() 3675 self._match_text_seq("INCREMENT") 3676 increment = self._parse_bitwise() 3677 3678 if start and increment: 3679 return exp.GeneratedAsIdentityColumnConstraint(start=start, increment=increment) 3680 3681 return exp.AutoIncrementColumnConstraint() 3682 3683 def _parse_compress(self) -> exp.CompressColumnConstraint: 3684 if self._match(TokenType.L_PAREN, advance=False): 3685 return self.expression( 3686 exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise) 3687 ) 3688 3689 return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise()) 3690 3691 def _parse_generated_as_identity(self) -> exp.GeneratedAsIdentityColumnConstraint: 3692 if self._match_text_seq("BY", "DEFAULT"): 3693 on_null = self._match_pair(TokenType.ON, TokenType.NULL) 3694 this = self.expression( 3695 exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null 3696 ) 3697 else: 3698 self._match_text_seq("ALWAYS") 3699 this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True) 3700 3701 self._match(TokenType.ALIAS) 3702 identity = self._match_text_seq("IDENTITY") 3703 3704 if self._match(TokenType.L_PAREN): 3705 if self._match(TokenType.START_WITH): 3706 this.set("start", self._parse_bitwise()) 3707 if self._match_text_seq("INCREMENT", "BY"): 3708 this.set("increment", self._parse_bitwise()) 3709 if self._match_text_seq("MINVALUE"): 3710 this.set("minvalue", self._parse_bitwise()) 3711 if self._match_text_seq("MAXVALUE"): 3712 this.set("maxvalue", self._parse_bitwise()) 3713 3714 if self._match_text_seq("CYCLE"): 3715 this.set("cycle", True) 3716 elif self._match_text_seq("NO", "CYCLE"): 3717 this.set("cycle", False) 3718 3719 if not identity: 3720 this.set("expression", self._parse_bitwise()) 3721 3722 self._match_r_paren() 3723 3724 return this 3725 3726 def _parse_inline(self) -> exp.InlineLengthColumnConstraint: 3727 self._match_text_seq("LENGTH") 3728 return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise()) 3729 3730 def _parse_not_constraint( 3731 self, 3732 ) -> t.Optional[exp.Expression]: 3733 if self._match_text_seq("NULL"): 3734 return self.expression(exp.NotNullColumnConstraint) 3735 if self._match_text_seq("CASESPECIFIC"): 3736 return self.expression(exp.CaseSpecificColumnConstraint, not_=True) 3737 if self._match_text_seq("FOR", "REPLICATION"): 3738 return self.expression(exp.NotForReplicationColumnConstraint) 3739 return None 3740 3741 def _parse_column_constraint(self) -> t.Optional[exp.Expression]: 3742 if self._match(TokenType.CONSTRAINT): 3743 this = self._parse_id_var() 3744 else: 3745 this = None 3746 3747 if self._match_texts(self.CONSTRAINT_PARSERS): 3748 return self.expression( 3749 exp.ColumnConstraint, 3750 this=this, 3751 kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self), 3752 ) 3753 3754 return this 3755 3756 def _parse_constraint(self) -> t.Optional[exp.Expression]: 3757 if not self._match(TokenType.CONSTRAINT): 3758 return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS) 3759 3760 this = self._parse_id_var() 3761 expressions = [] 3762 3763 while True: 3764 constraint = self._parse_unnamed_constraint() or self._parse_function() 3765 if not constraint: 3766 break 3767 expressions.append(constraint) 3768 3769 return self.expression(exp.Constraint, this=this, expressions=expressions) 3770 3771 def _parse_unnamed_constraint( 3772 self, constraints: t.Optional[t.Collection[str]] = None 3773 ) -> t.Optional[exp.Expression]: 3774 if not self._match_texts(constraints or self.CONSTRAINT_PARSERS): 3775 return None 3776 3777 constraint = self._prev.text.upper() 3778 if constraint not in self.CONSTRAINT_PARSERS: 3779 self.raise_error(f"No parser found for schema constraint {constraint}.") 3780 3781 return self.CONSTRAINT_PARSERS[constraint](self) 3782 3783 def _parse_unique(self) -> exp.UniqueColumnConstraint: 3784 self._match_text_seq("KEY") 3785 return self.expression( 3786 exp.UniqueColumnConstraint, this=self._parse_schema(self._parse_id_var(any_token=False)) 3787 ) 3788 3789 def _parse_key_constraint_options(self) -> t.List[str]: 3790 options = [] 3791 while True: 3792 if not self._curr: 3793 break 3794 3795 if self._match(TokenType.ON): 3796 action = None 3797 on = self._advance_any() and self._prev.text 3798 3799 if self._match_text_seq("NO", "ACTION"): 3800 action = "NO ACTION" 3801 elif self._match_text_seq("CASCADE"): 3802 action = "CASCADE" 3803 elif self._match_pair(TokenType.SET, TokenType.NULL): 3804 action = "SET NULL" 3805 elif self._match_pair(TokenType.SET, TokenType.DEFAULT): 3806 action = "SET DEFAULT" 3807 else: 3808 self.raise_error("Invalid key constraint") 3809 3810 options.append(f"ON {on} {action}") 3811 elif self._match_text_seq("NOT", "ENFORCED"): 3812 options.append("NOT ENFORCED") 3813 elif self._match_text_seq("DEFERRABLE"): 3814 options.append("DEFERRABLE") 3815 elif self._match_text_seq("INITIALLY", "DEFERRED"): 3816 options.append("INITIALLY DEFERRED") 3817 elif self._match_text_seq("NORELY"): 3818 options.append("NORELY") 3819 elif self._match_text_seq("MATCH", "FULL"): 3820 options.append("MATCH FULL") 3821 else: 3822 break 3823 3824 return options 3825 3826 def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]: 3827 if match and not self._match(TokenType.REFERENCES): 3828 return None 3829 3830 expressions = None 3831 this = self._parse_table(schema=True) 3832 options = self._parse_key_constraint_options() 3833 return self.expression(exp.Reference, this=this, expressions=expressions, options=options) 3834 3835 def _parse_foreign_key(self) -> exp.ForeignKey: 3836 expressions = self._parse_wrapped_id_vars() 3837 reference = self._parse_references() 3838 options = {} 3839 3840 while self._match(TokenType.ON): 3841 if not self._match_set((TokenType.DELETE, TokenType.UPDATE)): 3842 self.raise_error("Expected DELETE or UPDATE") 3843 3844 kind = self._prev.text.lower() 3845 3846 if self._match_text_seq("NO", "ACTION"): 3847 action = "NO ACTION" 3848 elif self._match(TokenType.SET): 3849 self._match_set((TokenType.NULL, TokenType.DEFAULT)) 3850 action = "SET " + self._prev.text.upper() 3851 else: 3852 self._advance() 3853 action = self._prev.text.upper() 3854 3855 options[kind] = action 3856 3857 return self.expression( 3858 exp.ForeignKey, expressions=expressions, reference=reference, **options # type: ignore 3859 ) 3860 3861 def _parse_primary_key( 3862 self, wrapped_optional: bool = False, in_props: bool = False 3863 ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey: 3864 desc = ( 3865 self._match_set((TokenType.ASC, TokenType.DESC)) 3866 and self._prev.token_type == TokenType.DESC 3867 ) 3868 3869 if not in_props and not self._match(TokenType.L_PAREN, advance=False): 3870 return self.expression(exp.PrimaryKeyColumnConstraint, desc=desc) 3871 3872 expressions = self._parse_wrapped_csv(self._parse_field, optional=wrapped_optional) 3873 options = self._parse_key_constraint_options() 3874 return self.expression(exp.PrimaryKey, expressions=expressions, options=options) 3875 3876 def _parse_bracket(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3877 if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)): 3878 return this 3879 3880 bracket_kind = self._prev.token_type 3881 3882 if self._match(TokenType.COLON): 3883 expressions: t.List[exp.Expression] = [ 3884 self.expression(exp.Slice, expression=self._parse_conjunction()) 3885 ] 3886 else: 3887 expressions = self._parse_csv( 3888 lambda: self._parse_slice( 3889 self._parse_alias(self._parse_conjunction(), explicit=True) 3890 ) 3891 ) 3892 3893 # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs 3894 if bracket_kind == TokenType.L_BRACE: 3895 this = self.expression(exp.Struct, expressions=expressions) 3896 elif not this or this.name.upper() == "ARRAY": 3897 this = self.expression(exp.Array, expressions=expressions) 3898 else: 3899 expressions = apply_index_offset(this, expressions, -self.INDEX_OFFSET) 3900 this = self.expression(exp.Bracket, this=this, expressions=expressions) 3901 3902 if not self._match(TokenType.R_BRACKET) and bracket_kind == TokenType.L_BRACKET: 3903 self.raise_error("Expected ]") 3904 elif not self._match(TokenType.R_BRACE) and bracket_kind == TokenType.L_BRACE: 3905 self.raise_error("Expected }") 3906 3907 self._add_comments(this) 3908 return self._parse_bracket(this) 3909 3910 def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3911 if self._match(TokenType.COLON): 3912 return self.expression(exp.Slice, this=this, expression=self._parse_conjunction()) 3913 return this 3914 3915 def _parse_case(self) -> t.Optional[exp.Expression]: 3916 ifs = [] 3917 default = None 3918 3919 comments = self._prev_comments 3920 expression = self._parse_conjunction() 3921 3922 while self._match(TokenType.WHEN): 3923 this = self._parse_conjunction() 3924 self._match(TokenType.THEN) 3925 then = self._parse_conjunction() 3926 ifs.append(self.expression(exp.If, this=this, true=then)) 3927 3928 if self._match(TokenType.ELSE): 3929 default = self._parse_conjunction() 3930 3931 if not self._match(TokenType.END): 3932 self.raise_error("Expected END after CASE", self._prev) 3933 3934 return self._parse_window( 3935 self.expression(exp.Case, comments=comments, this=expression, ifs=ifs, default=default) 3936 ) 3937 3938 def _parse_if(self) -> t.Optional[exp.Expression]: 3939 if self._match(TokenType.L_PAREN): 3940 args = self._parse_csv(self._parse_conjunction) 3941 this = self.validate_expression(exp.If.from_arg_list(args), args) 3942 self._match_r_paren() 3943 else: 3944 index = self._index - 1 3945 condition = self._parse_conjunction() 3946 3947 if not condition: 3948 self._retreat(index) 3949 return None 3950 3951 self._match(TokenType.THEN) 3952 true = self._parse_conjunction() 3953 false = self._parse_conjunction() if self._match(TokenType.ELSE) else None 3954 self._match(TokenType.END) 3955 this = self.expression(exp.If, this=condition, true=true, false=false) 3956 3957 return self._parse_window(this) 3958 3959 def _parse_next_value_for(self) -> t.Optional[exp.Expression]: 3960 if not self._match_text_seq("VALUE", "FOR"): 3961 self._retreat(self._index - 1) 3962 return None 3963 3964 return self.expression( 3965 exp.NextValueFor, 3966 this=self._parse_column(), 3967 order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order), 3968 ) 3969 3970 def _parse_extract(self) -> exp.Extract: 3971 this = self._parse_function() or self._parse_var() or self._parse_type() 3972 3973 if self._match(TokenType.FROM): 3974 return self.expression(exp.Extract, this=this, expression=self._parse_bitwise()) 3975 3976 if not self._match(TokenType.COMMA): 3977 self.raise_error("Expected FROM or comma after EXTRACT", self._prev) 3978 3979 return self.expression(exp.Extract, this=this, expression=self._parse_bitwise()) 3980 3981 def _parse_any_value(self) -> exp.AnyValue: 3982 this = self._parse_lambda() 3983 is_max = None 3984 having = None 3985 3986 if self._match(TokenType.HAVING): 3987 self._match_texts(("MAX", "MIN")) 3988 is_max = self._prev.text == "MAX" 3989 having = self._parse_column() 3990 3991 return self.expression(exp.AnyValue, this=this, having=having, max=is_max) 3992 3993 def _parse_cast(self, strict: bool) -> exp.Expression: 3994 this = self._parse_conjunction() 3995 3996 if not self._match(TokenType.ALIAS): 3997 if self._match(TokenType.COMMA): 3998 return self.expression(exp.CastToStrType, this=this, to=self._parse_string()) 3999 4000 self.raise_error("Expected AS after CAST") 4001 4002 fmt = None 4003 to = self._parse_types() 4004 4005 if not to: 4006 self.raise_error("Expected TYPE after CAST") 4007 elif isinstance(to, exp.Identifier): 4008 to = exp.DataType.build(to.name, udt=True) 4009 elif to.this == exp.DataType.Type.CHAR: 4010 if self._match(TokenType.CHARACTER_SET): 4011 to = self.expression(exp.CharacterSet, this=self._parse_var_or_string()) 4012 elif self._match(TokenType.FORMAT): 4013 fmt_string = self._parse_string() 4014 fmt = self._parse_at_time_zone(fmt_string) 4015 4016 if to.this in exp.DataType.TEMPORAL_TYPES: 4017 this = self.expression( 4018 exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime, 4019 this=this, 4020 format=exp.Literal.string( 4021 format_time( 4022 fmt_string.this if fmt_string else "", 4023 self.FORMAT_MAPPING or self.TIME_MAPPING, 4024 self.FORMAT_TRIE or self.TIME_TRIE, 4025 ) 4026 ), 4027 ) 4028 4029 if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime): 4030 this.set("zone", fmt.args["zone"]) 4031 4032 return this 4033 4034 return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, format=fmt) 4035 4036 def _parse_concat(self) -> t.Optional[exp.Expression]: 4037 args = self._parse_csv(self._parse_conjunction) 4038 if self.CONCAT_NULL_OUTPUTS_STRING: 4039 args = [ 4040 exp.func("COALESCE", exp.cast(arg, "text"), exp.Literal.string("")) 4041 for arg in args 4042 if arg 4043 ] 4044 4045 # Some dialects (e.g. Trino) don't allow a single-argument CONCAT call, so when 4046 # we find such a call we replace it with its argument. 4047 if len(args) == 1: 4048 return args[0] 4049 4050 return self.expression( 4051 exp.Concat if self.STRICT_STRING_CONCAT else exp.SafeConcat, expressions=args 4052 ) 4053 4054 def _parse_string_agg(self) -> exp.Expression: 4055 if self._match(TokenType.DISTINCT): 4056 args: t.List[t.Optional[exp.Expression]] = [ 4057 self.expression(exp.Distinct, expressions=[self._parse_conjunction()]) 4058 ] 4059 if self._match(TokenType.COMMA): 4060 args.extend(self._parse_csv(self._parse_conjunction)) 4061 else: 4062 args = self._parse_csv(self._parse_conjunction) # type: ignore 4063 4064 index = self._index 4065 if not self._match(TokenType.R_PAREN) and args: 4066 # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]]) 4067 # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n]) 4068 args[-1] = self._parse_limit(this=self._parse_order(this=args[-1])) 4069 return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1)) 4070 4071 # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]). 4072 # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that 4073 # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them. 4074 if not self._match_text_seq("WITHIN", "GROUP"): 4075 self._retreat(index) 4076 return self.validate_expression(exp.GroupConcat.from_arg_list(args), args) 4077 4078 self._match_l_paren() # The corresponding match_r_paren will be called in parse_function (caller) 4079 order = self._parse_order(this=seq_get(args, 0)) 4080 return self.expression(exp.GroupConcat, this=order, separator=seq_get(args, 1)) 4081 4082 def _parse_convert(self, strict: bool) -> t.Optional[exp.Expression]: 4083 this = self._parse_bitwise() 4084 4085 if self._match(TokenType.USING): 4086 to: t.Optional[exp.Expression] = self.expression( 4087 exp.CharacterSet, this=self._parse_var() 4088 ) 4089 elif self._match(TokenType.COMMA): 4090 to = self._parse_types() 4091 else: 4092 to = None 4093 4094 return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to) 4095 4096 def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]: 4097 """ 4098 There are generally two variants of the DECODE function: 4099 4100 - DECODE(bin, charset) 4101 - DECODE(expression, search, result [, search, result] ... [, default]) 4102 4103 The second variant will always be parsed into a CASE expression. Note that NULL 4104 needs special treatment, since we need to explicitly check for it with `IS NULL`, 4105 instead of relying on pattern matching. 4106 """ 4107 args = self._parse_csv(self._parse_conjunction) 4108 4109 if len(args) < 3: 4110 return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1)) 4111 4112 expression, *expressions = args 4113 if not expression: 4114 return None 4115 4116 ifs = [] 4117 for search, result in zip(expressions[::2], expressions[1::2]): 4118 if not search or not result: 4119 return None 4120 4121 if isinstance(search, exp.Literal): 4122 ifs.append( 4123 exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result) 4124 ) 4125 elif isinstance(search, exp.Null): 4126 ifs.append( 4127 exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result) 4128 ) 4129 else: 4130 cond = exp.or_( 4131 exp.EQ(this=expression.copy(), expression=search), 4132 exp.and_( 4133 exp.Is(this=expression.copy(), expression=exp.Null()), 4134 exp.Is(this=search.copy(), expression=exp.Null()), 4135 copy=False, 4136 ), 4137 copy=False, 4138 ) 4139 ifs.append(exp.If(this=cond, true=result)) 4140 4141 return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None) 4142 4143 def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]: 4144 self._match_text_seq("KEY") 4145 key = self._parse_field() 4146 self._match_set((TokenType.COLON, TokenType.COMMA)) 4147 self._match_text_seq("VALUE") 4148 value = self._parse_column() 4149 4150 if not key and not value: 4151 return None 4152 return self.expression(exp.JSONKeyValue, this=key, expression=value) 4153 4154 def _parse_json_object(self) -> exp.JSONObject: 4155 star = self._parse_star() 4156 expressions = [star] if star else self._parse_csv(self._parse_json_key_value) 4157 4158 null_handling = None 4159 if self._match_text_seq("NULL", "ON", "NULL"): 4160 null_handling = "NULL ON NULL" 4161 elif self._match_text_seq("ABSENT", "ON", "NULL"): 4162 null_handling = "ABSENT ON NULL" 4163 4164 unique_keys = None 4165 if self._match_text_seq("WITH", "UNIQUE"): 4166 unique_keys = True 4167 elif self._match_text_seq("WITHOUT", "UNIQUE"): 4168 unique_keys = False 4169 4170 self._match_text_seq("KEYS") 4171 4172 return_type = self._match_text_seq("RETURNING") and self._parse_type() 4173 format_json = self._match_text_seq("FORMAT", "JSON") 4174 encoding = self._match_text_seq("ENCODING") and self._parse_var() 4175 4176 return self.expression( 4177 exp.JSONObject, 4178 expressions=expressions, 4179 null_handling=null_handling, 4180 unique_keys=unique_keys, 4181 return_type=return_type, 4182 format_json=format_json, 4183 encoding=encoding, 4184 ) 4185 4186 def _parse_logarithm(self) -> exp.Func: 4187 # Default argument order is base, expression 4188 args = self._parse_csv(self._parse_range) 4189 4190 if len(args) > 1: 4191 if not self.LOG_BASE_FIRST: 4192 args.reverse() 4193 return exp.Log.from_arg_list(args) 4194 4195 return self.expression( 4196 exp.Ln if self.LOG_DEFAULTS_TO_LN else exp.Log, this=seq_get(args, 0) 4197 ) 4198 4199 def _parse_match_against(self) -> exp.MatchAgainst: 4200 expressions = self._parse_csv(self._parse_column) 4201 4202 self._match_text_seq(")", "AGAINST", "(") 4203 4204 this = self._parse_string() 4205 4206 if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"): 4207 modifier = "IN NATURAL LANGUAGE MODE" 4208 if self._match_text_seq("WITH", "QUERY", "EXPANSION"): 4209 modifier = f"{modifier} WITH QUERY EXPANSION" 4210 elif self._match_text_seq("IN", "BOOLEAN", "MODE"): 4211 modifier = "IN BOOLEAN MODE" 4212 elif self._match_text_seq("WITH", "QUERY", "EXPANSION"): 4213 modifier = "WITH QUERY EXPANSION" 4214 else: 4215 modifier = None 4216 4217 return self.expression( 4218 exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier 4219 ) 4220 4221 # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16 4222 def _parse_open_json(self) -> exp.OpenJSON: 4223 this = self._parse_bitwise() 4224 path = self._match(TokenType.COMMA) and self._parse_string() 4225 4226 def _parse_open_json_column_def() -> exp.OpenJSONColumnDef: 4227 this = self._parse_field(any_token=True) 4228 kind = self._parse_types() 4229 path = self._parse_string() 4230 as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON) 4231 4232 return self.expression( 4233 exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json 4234 ) 4235 4236 expressions = None 4237 if self._match_pair(TokenType.R_PAREN, TokenType.WITH): 4238 self._match_l_paren() 4239 expressions = self._parse_csv(_parse_open_json_column_def) 4240 4241 return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions) 4242 4243 def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition: 4244 args = self._parse_csv(self._parse_bitwise) 4245 4246 if self._match(TokenType.IN): 4247 return self.expression( 4248 exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0) 4249 ) 4250 4251 if haystack_first: 4252 haystack = seq_get(args, 0) 4253 needle = seq_get(args, 1) 4254 else: 4255 needle = seq_get(args, 0) 4256 haystack = seq_get(args, 1) 4257 4258 return self.expression( 4259 exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2) 4260 ) 4261 4262 def _parse_join_hint(self, func_name: str) -> exp.JoinHint: 4263 args = self._parse_csv(self._parse_table) 4264 return exp.JoinHint(this=func_name.upper(), expressions=args) 4265 4266 def _parse_substring(self) -> exp.Substring: 4267 # Postgres supports the form: substring(string [from int] [for int]) 4268 # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6 4269 4270 args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise)) 4271 4272 if self._match(TokenType.FROM): 4273 args.append(self._parse_bitwise()) 4274 if self._match(TokenType.FOR): 4275 args.append(self._parse_bitwise()) 4276 4277 return self.validate_expression(exp.Substring.from_arg_list(args), args) 4278 4279 def _parse_trim(self) -> exp.Trim: 4280 # https://www.w3resource.com/sql/character-functions/trim.php 4281 # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html 4282 4283 position = None 4284 collation = None 4285 4286 if self._match_texts(self.TRIM_TYPES): 4287 position = self._prev.text.upper() 4288 4289 expression = self._parse_bitwise() 4290 if self._match_set((TokenType.FROM, TokenType.COMMA)): 4291 this = self._parse_bitwise() 4292 else: 4293 this = expression 4294 expression = None 4295 4296 if self._match(TokenType.COLLATE): 4297 collation = self._parse_bitwise() 4298 4299 return self.expression( 4300 exp.Trim, this=this, position=position, expression=expression, collation=collation 4301 ) 4302 4303 def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]: 4304 return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window) 4305 4306 def _parse_named_window(self) -> t.Optional[exp.Expression]: 4307 return self._parse_window(self._parse_id_var(), alias=True) 4308 4309 def _parse_respect_or_ignore_nulls( 4310 self, this: t.Optional[exp.Expression] 4311 ) -> t.Optional[exp.Expression]: 4312 if self._match_text_seq("IGNORE", "NULLS"): 4313 return self.expression(exp.IgnoreNulls, this=this) 4314 if self._match_text_seq("RESPECT", "NULLS"): 4315 return self.expression(exp.RespectNulls, this=this) 4316 return this 4317 4318 def _parse_window( 4319 self, this: t.Optional[exp.Expression], alias: bool = False 4320 ) -> t.Optional[exp.Expression]: 4321 if self._match_pair(TokenType.FILTER, TokenType.L_PAREN): 4322 self._match(TokenType.WHERE) 4323 this = self.expression( 4324 exp.Filter, this=this, expression=self._parse_where(skip_where_token=True) 4325 ) 4326 self._match_r_paren() 4327 4328 # T-SQL allows the OVER (...) syntax after WITHIN GROUP. 4329 # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16 4330 if self._match_text_seq("WITHIN", "GROUP"): 4331 order = self._parse_wrapped(self._parse_order) 4332 this = self.expression(exp.WithinGroup, this=this, expression=order) 4333 4334 # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER 4335 # Some dialects choose to implement and some do not. 4336 # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html 4337 4338 # There is some code above in _parse_lambda that handles 4339 # SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ... 4340 4341 # The below changes handle 4342 # SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ... 4343 4344 # Oracle allows both formats 4345 # (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html) 4346 # and Snowflake chose to do the same for familiarity 4347 # https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes 4348 this = self._parse_respect_or_ignore_nulls(this) 4349 4350 # bigquery select from window x AS (partition by ...) 4351 if alias: 4352 over = None 4353 self._match(TokenType.ALIAS) 4354 elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS): 4355 return this 4356 else: 4357 over = self._prev.text.upper() 4358 4359 if not self._match(TokenType.L_PAREN): 4360 return self.expression( 4361 exp.Window, this=this, alias=self._parse_id_var(False), over=over 4362 ) 4363 4364 window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS) 4365 4366 first = self._match(TokenType.FIRST) 4367 if self._match_text_seq("LAST"): 4368 first = False 4369 4370 partition, order = self._parse_partition_and_order() 4371 kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text 4372 4373 if kind: 4374 self._match(TokenType.BETWEEN) 4375 start = self._parse_window_spec() 4376 self._match(TokenType.AND) 4377 end = self._parse_window_spec() 4378 4379 spec = self.expression( 4380 exp.WindowSpec, 4381 kind=kind, 4382 start=start["value"], 4383 start_side=start["side"], 4384 end=end["value"], 4385 end_side=end["side"], 4386 ) 4387 else: 4388 spec = None 4389 4390 self._match_r_paren() 4391 4392 window = self.expression( 4393 exp.Window, 4394 this=this, 4395 partition_by=partition, 4396 order=order, 4397 spec=spec, 4398 alias=window_alias, 4399 over=over, 4400 first=first, 4401 ) 4402 4403 # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...) 4404 if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False): 4405 return self._parse_window(window, alias=alias) 4406 4407 return window 4408 4409 def _parse_partition_and_order( 4410 self, 4411 ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]: 4412 return self._parse_partition_by(), self._parse_order() 4413 4414 def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]: 4415 self._match(TokenType.BETWEEN) 4416 4417 return { 4418 "value": ( 4419 (self._match_text_seq("UNBOUNDED") and "UNBOUNDED") 4420 or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW") 4421 or self._parse_bitwise() 4422 ), 4423 "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text, 4424 } 4425 4426 def _parse_alias( 4427 self, this: t.Optional[exp.Expression], explicit: bool = False 4428 ) -> t.Optional[exp.Expression]: 4429 any_token = self._match(TokenType.ALIAS) 4430 4431 if explicit and not any_token: 4432 return this 4433 4434 if self._match(TokenType.L_PAREN): 4435 aliases = self.expression( 4436 exp.Aliases, 4437 this=this, 4438 expressions=self._parse_csv(lambda: self._parse_id_var(any_token)), 4439 ) 4440 self._match_r_paren(aliases) 4441 return aliases 4442 4443 alias = self._parse_id_var(any_token) 4444 4445 if alias: 4446 return self.expression(exp.Alias, this=this, alias=alias) 4447 4448 return this 4449 4450 def _parse_id_var( 4451 self, 4452 any_token: bool = True, 4453 tokens: t.Optional[t.Collection[TokenType]] = None, 4454 ) -> t.Optional[exp.Expression]: 4455 identifier = self._parse_identifier() 4456 4457 if identifier: 4458 return identifier 4459 4460 if (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS): 4461 quoted = self._prev.token_type == TokenType.STRING 4462 return exp.Identifier(this=self._prev.text, quoted=quoted) 4463 4464 return None 4465 4466 def _parse_string(self) -> t.Optional[exp.Expression]: 4467 if self._match(TokenType.STRING): 4468 return self.PRIMARY_PARSERS[TokenType.STRING](self, self._prev) 4469 return self._parse_placeholder() 4470 4471 def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]: 4472 return exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True) 4473 4474 def _parse_number(self) -> t.Optional[exp.Expression]: 4475 if self._match(TokenType.NUMBER): 4476 return self.PRIMARY_PARSERS[TokenType.NUMBER](self, self._prev) 4477 return self._parse_placeholder() 4478 4479 def _parse_identifier(self) -> t.Optional[exp.Expression]: 4480 if self._match(TokenType.IDENTIFIER): 4481 return self.expression(exp.Identifier, this=self._prev.text, quoted=True) 4482 return self._parse_placeholder() 4483 4484 def _parse_var( 4485 self, any_token: bool = False, tokens: t.Optional[t.Collection[TokenType]] = None 4486 ) -> t.Optional[exp.Expression]: 4487 if ( 4488 (any_token and self._advance_any()) 4489 or self._match(TokenType.VAR) 4490 or (self._match_set(tokens) if tokens else False) 4491 ): 4492 return self.expression(exp.Var, this=self._prev.text) 4493 return self._parse_placeholder() 4494 4495 def _advance_any(self) -> t.Optional[Token]: 4496 if self._curr and self._curr.token_type not in self.RESERVED_KEYWORDS: 4497 self._advance() 4498 return self._prev 4499 return None 4500 4501 def _parse_var_or_string(self) -> t.Optional[exp.Expression]: 4502 return self._parse_var() or self._parse_string() 4503 4504 def _parse_null(self) -> t.Optional[exp.Expression]: 4505 if self._match(TokenType.NULL): 4506 return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev) 4507 return self._parse_placeholder() 4508 4509 def _parse_boolean(self) -> t.Optional[exp.Expression]: 4510 if self._match(TokenType.TRUE): 4511 return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev) 4512 if self._match(TokenType.FALSE): 4513 return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev) 4514 return self._parse_placeholder() 4515 4516 def _parse_star(self) -> t.Optional[exp.Expression]: 4517 if self._match(TokenType.STAR): 4518 return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev) 4519 return self._parse_placeholder() 4520 4521 def _parse_parameter(self) -> exp.Parameter: 4522 wrapped = self._match(TokenType.L_BRACE) 4523 this = self._parse_var() or self._parse_identifier() or self._parse_primary() 4524 self._match(TokenType.R_BRACE) 4525 return self.expression(exp.Parameter, this=this, wrapped=wrapped) 4526 4527 def _parse_placeholder(self) -> t.Optional[exp.Expression]: 4528 if self._match_set(self.PLACEHOLDER_PARSERS): 4529 placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self) 4530 if placeholder: 4531 return placeholder 4532 self._advance(-1) 4533 return None 4534 4535 def _parse_except(self) -> t.Optional[t.List[exp.Expression]]: 4536 if not self._match(TokenType.EXCEPT): 4537 return None 4538 if self._match(TokenType.L_PAREN, advance=False): 4539 return self._parse_wrapped_csv(self._parse_column) 4540 return self._parse_csv(self._parse_column) 4541 4542 def _parse_replace(self) -> t.Optional[t.List[exp.Expression]]: 4543 if not self._match(TokenType.REPLACE): 4544 return None 4545 if self._match(TokenType.L_PAREN, advance=False): 4546 return self._parse_wrapped_csv(self._parse_expression) 4547 return self._parse_expressions() 4548 4549 def _parse_csv( 4550 self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA 4551 ) -> t.List[exp.Expression]: 4552 parse_result = parse_method() 4553 items = [parse_result] if parse_result is not None else [] 4554 4555 while self._match(sep): 4556 self._add_comments(parse_result) 4557 parse_result = parse_method() 4558 if parse_result is not None: 4559 items.append(parse_result) 4560 4561 return items 4562 4563 def _parse_tokens( 4564 self, parse_method: t.Callable, expressions: t.Dict 4565 ) -> t.Optional[exp.Expression]: 4566 this = parse_method() 4567 4568 while self._match_set(expressions): 4569 this = self.expression( 4570 expressions[self._prev.token_type], 4571 this=this, 4572 comments=self._prev_comments, 4573 expression=parse_method(), 4574 ) 4575 4576 return this 4577 4578 def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]: 4579 return self._parse_wrapped_csv(self._parse_id_var, optional=optional) 4580 4581 def _parse_wrapped_csv( 4582 self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False 4583 ) -> t.List[exp.Expression]: 4584 return self._parse_wrapped( 4585 lambda: self._parse_csv(parse_method, sep=sep), optional=optional 4586 ) 4587 4588 def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any: 4589 wrapped = self._match(TokenType.L_PAREN) 4590 if not wrapped and not optional: 4591 self.raise_error("Expecting (") 4592 parse_result = parse_method() 4593 if wrapped: 4594 self._match_r_paren() 4595 return parse_result 4596 4597 def _parse_expressions(self) -> t.List[exp.Expression]: 4598 return self._parse_csv(self._parse_expression) 4599 4600 def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]: 4601 return self._parse_select() or self._parse_set_operations( 4602 self._parse_expression() if alias else self._parse_conjunction() 4603 ) 4604 4605 def _parse_ddl_select(self) -> t.Optional[exp.Expression]: 4606 return self._parse_query_modifiers( 4607 self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False)) 4608 ) 4609 4610 def _parse_transaction(self) -> exp.Transaction | exp.Command: 4611 this = None 4612 if self._match_texts(self.TRANSACTION_KIND): 4613 this = self._prev.text 4614 4615 self._match_texts({"TRANSACTION", "WORK"}) 4616 4617 modes = [] 4618 while True: 4619 mode = [] 4620 while self._match(TokenType.VAR): 4621 mode.append(self._prev.text) 4622 4623 if mode: 4624 modes.append(" ".join(mode)) 4625 if not self._match(TokenType.COMMA): 4626 break 4627 4628 return self.expression(exp.Transaction, this=this, modes=modes) 4629 4630 def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback: 4631 chain = None 4632 savepoint = None 4633 is_rollback = self._prev.token_type == TokenType.ROLLBACK 4634 4635 self._match_texts({"TRANSACTION", "WORK"}) 4636 4637 if self._match_text_seq("TO"): 4638 self._match_text_seq("SAVEPOINT") 4639 savepoint = self._parse_id_var() 4640 4641 if self._match(TokenType.AND): 4642 chain = not self._match_text_seq("NO") 4643 self._match_text_seq("CHAIN") 4644 4645 if is_rollback: 4646 return self.expression(exp.Rollback, savepoint=savepoint) 4647 4648 return self.expression(exp.Commit, chain=chain) 4649 4650 def _parse_add_column(self) -> t.Optional[exp.Expression]: 4651 if not self._match_text_seq("ADD"): 4652 return None 4653 4654 self._match(TokenType.COLUMN) 4655 exists_column = self._parse_exists(not_=True) 4656 expression = self._parse_field_def() 4657 4658 if expression: 4659 expression.set("exists", exists_column) 4660 4661 # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns 4662 if self._match_texts(("FIRST", "AFTER")): 4663 position = self._prev.text 4664 column_position = self.expression( 4665 exp.ColumnPosition, this=self._parse_column(), position=position 4666 ) 4667 expression.set("position", column_position) 4668 4669 return expression 4670 4671 def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]: 4672 drop = self._match(TokenType.DROP) and self._parse_drop() 4673 if drop and not isinstance(drop, exp.Command): 4674 drop.set("kind", drop.args.get("kind", "COLUMN")) 4675 return drop 4676 4677 # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html 4678 def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition: 4679 return self.expression( 4680 exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists 4681 ) 4682 4683 def _parse_add_constraint(self) -> exp.AddConstraint: 4684 this = None 4685 kind = self._prev.token_type 4686 4687 if kind == TokenType.CONSTRAINT: 4688 this = self._parse_id_var() 4689 4690 if self._match_text_seq("CHECK"): 4691 expression = self._parse_wrapped(self._parse_conjunction) 4692 enforced = self._match_text_seq("ENFORCED") 4693 4694 return self.expression( 4695 exp.AddConstraint, this=this, expression=expression, enforced=enforced 4696 ) 4697 4698 if kind == TokenType.FOREIGN_KEY or self._match(TokenType.FOREIGN_KEY): 4699 expression = self._parse_foreign_key() 4700 elif kind == TokenType.PRIMARY_KEY or self._match(TokenType.PRIMARY_KEY): 4701 expression = self._parse_primary_key() 4702 else: 4703 expression = None 4704 4705 return self.expression(exp.AddConstraint, this=this, expression=expression) 4706 4707 def _parse_alter_table_add(self) -> t.List[exp.Expression]: 4708 index = self._index - 1 4709 4710 if self._match_set(self.ADD_CONSTRAINT_TOKENS): 4711 return self._parse_csv(self._parse_add_constraint) 4712 4713 self._retreat(index) 4714 if not self.ALTER_TABLE_ADD_COLUMN_KEYWORD and self._match_text_seq("ADD"): 4715 return self._parse_csv(self._parse_field_def) 4716 4717 return self._parse_csv(self._parse_add_column) 4718 4719 def _parse_alter_table_alter(self) -> exp.AlterColumn: 4720 self._match(TokenType.COLUMN) 4721 column = self._parse_field(any_token=True) 4722 4723 if self._match_pair(TokenType.DROP, TokenType.DEFAULT): 4724 return self.expression(exp.AlterColumn, this=column, drop=True) 4725 if self._match_pair(TokenType.SET, TokenType.DEFAULT): 4726 return self.expression(exp.AlterColumn, this=column, default=self._parse_conjunction()) 4727 4728 self._match_text_seq("SET", "DATA") 4729 return self.expression( 4730 exp.AlterColumn, 4731 this=column, 4732 dtype=self._match_text_seq("TYPE") and self._parse_types(), 4733 collate=self._match(TokenType.COLLATE) and self._parse_term(), 4734 using=self._match(TokenType.USING) and self._parse_conjunction(), 4735 ) 4736 4737 def _parse_alter_table_drop(self) -> t.List[exp.Expression]: 4738 index = self._index - 1 4739 4740 partition_exists = self._parse_exists() 4741 if self._match(TokenType.PARTITION, advance=False): 4742 return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists)) 4743 4744 self._retreat(index) 4745 return self._parse_csv(self._parse_drop_column) 4746 4747 def _parse_alter_table_rename(self) -> exp.RenameTable: 4748 self._match_text_seq("TO") 4749 return self.expression(exp.RenameTable, this=self._parse_table(schema=True)) 4750 4751 def _parse_alter(self) -> exp.AlterTable | exp.Command: 4752 start = self._prev 4753 4754 if not self._match(TokenType.TABLE): 4755 return self._parse_as_command(start) 4756 4757 exists = self._parse_exists() 4758 this = self._parse_table(schema=True) 4759 4760 if self._next: 4761 self._advance() 4762 4763 parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None 4764 if parser: 4765 actions = ensure_list(parser(self)) 4766 4767 if not self._curr: 4768 return self.expression( 4769 exp.AlterTable, 4770 this=this, 4771 exists=exists, 4772 actions=actions, 4773 ) 4774 return self._parse_as_command(start) 4775 4776 def _parse_merge(self) -> exp.Merge: 4777 self._match(TokenType.INTO) 4778 target = self._parse_table() 4779 4780 if target and self._match(TokenType.ALIAS, advance=False): 4781 target.set("alias", self._parse_table_alias()) 4782 4783 self._match(TokenType.USING) 4784 using = self._parse_table() 4785 4786 self._match(TokenType.ON) 4787 on = self._parse_conjunction() 4788 4789 whens = [] 4790 while self._match(TokenType.WHEN): 4791 matched = not self._match(TokenType.NOT) 4792 self._match_text_seq("MATCHED") 4793 source = ( 4794 False 4795 if self._match_text_seq("BY", "TARGET") 4796 else self._match_text_seq("BY", "SOURCE") 4797 ) 4798 condition = self._parse_conjunction() if self._match(TokenType.AND) else None 4799 4800 self._match(TokenType.THEN) 4801 4802 if self._match(TokenType.INSERT): 4803 _this = self._parse_star() 4804 if _this: 4805 then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=_this) 4806 else: 4807 then = self.expression( 4808 exp.Insert, 4809 this=self._parse_value(), 4810 expression=self._match(TokenType.VALUES) and self._parse_value(), 4811 ) 4812 elif self._match(TokenType.UPDATE): 4813 expressions = self._parse_star() 4814 if expressions: 4815 then = self.expression(exp.Update, expressions=expressions) 4816 else: 4817 then = self.expression( 4818 exp.Update, 4819 expressions=self._match(TokenType.SET) 4820 and self._parse_csv(self._parse_equality), 4821 ) 4822 elif self._match(TokenType.DELETE): 4823 then = self.expression(exp.Var, this=self._prev.text) 4824 else: 4825 then = None 4826 4827 whens.append( 4828 self.expression( 4829 exp.When, 4830 matched=matched, 4831 source=source, 4832 condition=condition, 4833 then=then, 4834 ) 4835 ) 4836 4837 return self.expression( 4838 exp.Merge, 4839 this=target, 4840 using=using, 4841 on=on, 4842 expressions=whens, 4843 ) 4844 4845 def _parse_show(self) -> t.Optional[exp.Expression]: 4846 parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE) 4847 if parser: 4848 return parser(self) 4849 self._advance() 4850 return self.expression(exp.Show, this=self._prev.text.upper()) 4851 4852 def _parse_set_item_assignment( 4853 self, kind: t.Optional[str] = None 4854 ) -> t.Optional[exp.Expression]: 4855 index = self._index 4856 4857 if kind in {"GLOBAL", "SESSION"} and self._match_text_seq("TRANSACTION"): 4858 return self._parse_set_transaction(global_=kind == "GLOBAL") 4859 4860 left = self._parse_primary() or self._parse_id_var() 4861 4862 if not self._match_texts(("=", "TO")): 4863 self._retreat(index) 4864 return None 4865 4866 right = self._parse_statement() or self._parse_id_var() 4867 this = self.expression(exp.EQ, this=left, expression=right) 4868 4869 return self.expression(exp.SetItem, this=this, kind=kind) 4870 4871 def _parse_set_transaction(self, global_: bool = False) -> exp.Expression: 4872 self._match_text_seq("TRANSACTION") 4873 characteristics = self._parse_csv( 4874 lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS) 4875 ) 4876 return self.expression( 4877 exp.SetItem, 4878 expressions=characteristics, 4879 kind="TRANSACTION", 4880 **{"global": global_}, # type: ignore 4881 ) 4882 4883 def _parse_set_item(self) -> t.Optional[exp.Expression]: 4884 parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE) 4885 return parser(self) if parser else self._parse_set_item_assignment(kind=None) 4886 4887 def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command: 4888 index = self._index 4889 set_ = self.expression( 4890 exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag 4891 ) 4892 4893 if self._curr: 4894 self._retreat(index) 4895 return self._parse_as_command(self._prev) 4896 4897 return set_ 4898 4899 def _parse_var_from_options(self, options: t.Collection[str]) -> t.Optional[exp.Var]: 4900 for option in options: 4901 if self._match_text_seq(*option.split(" ")): 4902 return exp.var(option) 4903 return None 4904 4905 def _parse_as_command(self, start: Token) -> exp.Command: 4906 while self._curr: 4907 self._advance() 4908 text = self._find_sql(start, self._prev) 4909 size = len(start.text) 4910 return exp.Command(this=text[:size], expression=text[size:]) 4911 4912 def _parse_dict_property(self, this: str) -> exp.DictProperty: 4913 settings = [] 4914 4915 self._match_l_paren() 4916 kind = self._parse_id_var() 4917 4918 if self._match(TokenType.L_PAREN): 4919 while True: 4920 key = self._parse_id_var() 4921 value = self._parse_primary() 4922 4923 if not key and value is None: 4924 break 4925 settings.append(self.expression(exp.DictSubProperty, this=key, value=value)) 4926 self._match(TokenType.R_PAREN) 4927 4928 self._match_r_paren() 4929 4930 return self.expression( 4931 exp.DictProperty, 4932 this=this, 4933 kind=kind.this if kind else None, 4934 settings=settings, 4935 ) 4936 4937 def _parse_dict_range(self, this: str) -> exp.DictRange: 4938 self._match_l_paren() 4939 has_min = self._match_text_seq("MIN") 4940 if has_min: 4941 min = self._parse_var() or self._parse_primary() 4942 self._match_text_seq("MAX") 4943 max = self._parse_var() or self._parse_primary() 4944 else: 4945 max = self._parse_var() or self._parse_primary() 4946 min = exp.Literal.number(0) 4947 self._match_r_paren() 4948 return self.expression(exp.DictRange, this=this, min=min, max=max) 4949 4950 def _parse_comprehension(self, this: exp.Expression) -> exp.Comprehension: 4951 expression = self._parse_column() 4952 self._match(TokenType.IN) 4953 iterator = self._parse_column() 4954 condition = self._parse_conjunction() if self._match_text_seq("IF") else None 4955 return self.expression( 4956 exp.Comprehension, 4957 this=this, 4958 expression=expression, 4959 iterator=iterator, 4960 condition=condition, 4961 ) 4962 4963 def _find_parser( 4964 self, parsers: t.Dict[str, t.Callable], trie: t.Dict 4965 ) -> t.Optional[t.Callable]: 4966 if not self._curr: 4967 return None 4968 4969 index = self._index 4970 this = [] 4971 while True: 4972 # The current token might be multiple words 4973 curr = self._curr.text.upper() 4974 key = curr.split(" ") 4975 this.append(curr) 4976 4977 self._advance() 4978 result, trie = in_trie(trie, key) 4979 if result == TrieResult.FAILED: 4980 break 4981 4982 if result == TrieResult.EXISTS: 4983 subparser = parsers[" ".join(this)] 4984 return subparser 4985 4986 self._retreat(index) 4987 return None 4988 4989 def _match(self, token_type, advance=True, expression=None): 4990 if not self._curr: 4991 return None 4992 4993 if self._curr.token_type == token_type: 4994 if advance: 4995 self._advance() 4996 self._add_comments(expression) 4997 return True 4998 4999 return None 5000 5001 def _match_set(self, types, advance=True): 5002 if not self._curr: 5003 return None 5004 5005 if self._curr.token_type in types: 5006 if advance: 5007 self._advance() 5008 return True 5009 5010 return None 5011 5012 def _match_pair(self, token_type_a, token_type_b, advance=True): 5013 if not self._curr or not self._next: 5014 return None 5015 5016 if self._curr.token_type == token_type_a and self._next.token_type == token_type_b: 5017 if advance: 5018 self._advance(2) 5019 return True 5020 5021 return None 5022 5023 def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None: 5024 if not self._match(TokenType.L_PAREN, expression=expression): 5025 self.raise_error("Expecting (") 5026 5027 def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None: 5028 if not self._match(TokenType.R_PAREN, expression=expression): 5029 self.raise_error("Expecting )") 5030 5031 def _match_texts(self, texts, advance=True): 5032 if self._curr and self._curr.text.upper() in texts: 5033 if advance: 5034 self._advance() 5035 return True 5036 return False 5037 5038 def _match_text_seq(self, *texts, advance=True): 5039 index = self._index 5040 for text in texts: 5041 if self._curr and self._curr.text.upper() == text: 5042 self._advance() 5043 else: 5044 self._retreat(index) 5045 return False 5046 5047 if not advance: 5048 self._retreat(index) 5049 5050 return True 5051 5052 @t.overload 5053 def _replace_columns_with_dots(self, this: exp.Expression) -> exp.Expression: 5054 ... 5055 5056 @t.overload 5057 def _replace_columns_with_dots( 5058 self, this: t.Optional[exp.Expression] 5059 ) -> t.Optional[exp.Expression]: 5060 ... 5061 5062 def _replace_columns_with_dots(self, this): 5063 if isinstance(this, exp.Dot): 5064 exp.replace_children(this, self._replace_columns_with_dots) 5065 elif isinstance(this, exp.Column): 5066 exp.replace_children(this, self._replace_columns_with_dots) 5067 table = this.args.get("table") 5068 this = ( 5069 self.expression(exp.Dot, this=table, expression=this.this) if table else this.this 5070 ) 5071 5072 return this 5073 5074 def _replace_lambda( 5075 self, node: t.Optional[exp.Expression], lambda_variables: t.Set[str] 5076 ) -> t.Optional[exp.Expression]: 5077 if not node: 5078 return node 5079 5080 for column in node.find_all(exp.Column): 5081 if column.parts[0].name in lambda_variables: 5082 dot_or_id = column.to_dot() if column.table else column.this 5083 parent = column.parent 5084 5085 while isinstance(parent, exp.Dot): 5086 if not isinstance(parent.parent, exp.Dot): 5087 parent.replace(dot_or_id) 5088 break 5089 parent = parent.parent 5090 else: 5091 if column is node: 5092 node = dot_or_id 5093 else: 5094 column.replace(dot_or_id) 5095 return node
21def parse_var_map(args: t.List) -> exp.StarMap | exp.VarMap: 22 if len(args) == 1 and args[0].is_star: 23 return exp.StarMap(this=args[0]) 24 25 keys = [] 26 values = [] 27 for i in range(0, len(args), 2): 28 keys.append(args[i]) 29 values.append(args[i + 1]) 30 31 return exp.VarMap( 32 keys=exp.Array(expressions=keys), 33 values=exp.Array(expressions=values), 34 )
60class Parser(metaclass=_Parser): 61 """ 62 Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree. 63 64 Args: 65 error_level: The desired error level. 66 Default: ErrorLevel.IMMEDIATE 67 error_message_context: Determines the amount of context to capture from a 68 query string when displaying the error message (in number of characters). 69 Default: 100 70 max_errors: Maximum number of error messages to include in a raised ParseError. 71 This is only relevant if error_level is ErrorLevel.RAISE. 72 Default: 3 73 """ 74 75 FUNCTIONS: t.Dict[str, t.Callable] = { 76 **{name: f.from_arg_list for f in exp.ALL_FUNCTIONS for name in f.sql_names()}, 77 "DATE_TO_DATE_STR": lambda args: exp.Cast( 78 this=seq_get(args, 0), 79 to=exp.DataType(this=exp.DataType.Type.TEXT), 80 ), 81 "GLOB": lambda args: exp.Glob(this=seq_get(args, 1), expression=seq_get(args, 0)), 82 "LIKE": parse_like, 83 "TIME_TO_TIME_STR": lambda args: exp.Cast( 84 this=seq_get(args, 0), 85 to=exp.DataType(this=exp.DataType.Type.TEXT), 86 ), 87 "TS_OR_DS_TO_DATE_STR": lambda args: exp.Substring( 88 this=exp.Cast( 89 this=seq_get(args, 0), 90 to=exp.DataType(this=exp.DataType.Type.TEXT), 91 ), 92 start=exp.Literal.number(1), 93 length=exp.Literal.number(10), 94 ), 95 "VAR_MAP": parse_var_map, 96 } 97 98 NO_PAREN_FUNCTIONS = { 99 TokenType.CURRENT_DATE: exp.CurrentDate, 100 TokenType.CURRENT_DATETIME: exp.CurrentDate, 101 TokenType.CURRENT_TIME: exp.CurrentTime, 102 TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp, 103 TokenType.CURRENT_USER: exp.CurrentUser, 104 } 105 106 STRUCT_TYPE_TOKENS = { 107 TokenType.NESTED, 108 TokenType.STRUCT, 109 } 110 111 NESTED_TYPE_TOKENS = { 112 TokenType.ARRAY, 113 TokenType.LOWCARDINALITY, 114 TokenType.MAP, 115 TokenType.NULLABLE, 116 *STRUCT_TYPE_TOKENS, 117 } 118 119 ENUM_TYPE_TOKENS = { 120 TokenType.ENUM, 121 TokenType.ENUM8, 122 TokenType.ENUM16, 123 } 124 125 TYPE_TOKENS = { 126 TokenType.BIT, 127 TokenType.BOOLEAN, 128 TokenType.TINYINT, 129 TokenType.UTINYINT, 130 TokenType.SMALLINT, 131 TokenType.USMALLINT, 132 TokenType.INT, 133 TokenType.UINT, 134 TokenType.BIGINT, 135 TokenType.UBIGINT, 136 TokenType.INT128, 137 TokenType.UINT128, 138 TokenType.INT256, 139 TokenType.UINT256, 140 TokenType.MEDIUMINT, 141 TokenType.FIXEDSTRING, 142 TokenType.FLOAT, 143 TokenType.DOUBLE, 144 TokenType.CHAR, 145 TokenType.NCHAR, 146 TokenType.VARCHAR, 147 TokenType.NVARCHAR, 148 TokenType.TEXT, 149 TokenType.MEDIUMTEXT, 150 TokenType.LONGTEXT, 151 TokenType.MEDIUMBLOB, 152 TokenType.LONGBLOB, 153 TokenType.BINARY, 154 TokenType.VARBINARY, 155 TokenType.JSON, 156 TokenType.JSONB, 157 TokenType.INTERVAL, 158 TokenType.TIME, 159 TokenType.TIMETZ, 160 TokenType.TIMESTAMP, 161 TokenType.TIMESTAMPTZ, 162 TokenType.TIMESTAMPLTZ, 163 TokenType.DATETIME, 164 TokenType.DATETIME64, 165 TokenType.DATE, 166 TokenType.INT4RANGE, 167 TokenType.INT4MULTIRANGE, 168 TokenType.INT8RANGE, 169 TokenType.INT8MULTIRANGE, 170 TokenType.NUMRANGE, 171 TokenType.NUMMULTIRANGE, 172 TokenType.TSRANGE, 173 TokenType.TSMULTIRANGE, 174 TokenType.TSTZRANGE, 175 TokenType.TSTZMULTIRANGE, 176 TokenType.DATERANGE, 177 TokenType.DATEMULTIRANGE, 178 TokenType.DECIMAL, 179 TokenType.BIGDECIMAL, 180 TokenType.UUID, 181 TokenType.GEOGRAPHY, 182 TokenType.GEOMETRY, 183 TokenType.HLLSKETCH, 184 TokenType.HSTORE, 185 TokenType.PSEUDO_TYPE, 186 TokenType.SUPER, 187 TokenType.SERIAL, 188 TokenType.SMALLSERIAL, 189 TokenType.BIGSERIAL, 190 TokenType.XML, 191 TokenType.YEAR, 192 TokenType.UNIQUEIDENTIFIER, 193 TokenType.USERDEFINED, 194 TokenType.MONEY, 195 TokenType.SMALLMONEY, 196 TokenType.ROWVERSION, 197 TokenType.IMAGE, 198 TokenType.VARIANT, 199 TokenType.OBJECT, 200 TokenType.INET, 201 TokenType.IPADDRESS, 202 TokenType.IPPREFIX, 203 TokenType.UNKNOWN, 204 TokenType.NULL, 205 *ENUM_TYPE_TOKENS, 206 *NESTED_TYPE_TOKENS, 207 } 208 209 SUBQUERY_PREDICATES = { 210 TokenType.ANY: exp.Any, 211 TokenType.ALL: exp.All, 212 TokenType.EXISTS: exp.Exists, 213 TokenType.SOME: exp.Any, 214 } 215 216 RESERVED_KEYWORDS = { 217 *Tokenizer.SINGLE_TOKENS.values(), 218 TokenType.SELECT, 219 } 220 221 DB_CREATABLES = { 222 TokenType.DATABASE, 223 TokenType.SCHEMA, 224 TokenType.TABLE, 225 TokenType.VIEW, 226 TokenType.DICTIONARY, 227 } 228 229 CREATABLES = { 230 TokenType.COLUMN, 231 TokenType.FUNCTION, 232 TokenType.INDEX, 233 TokenType.PROCEDURE, 234 *DB_CREATABLES, 235 } 236 237 # Tokens that can represent identifiers 238 ID_VAR_TOKENS = { 239 TokenType.VAR, 240 TokenType.ANTI, 241 TokenType.APPLY, 242 TokenType.ASC, 243 TokenType.AUTO_INCREMENT, 244 TokenType.BEGIN, 245 TokenType.CACHE, 246 TokenType.CASE, 247 TokenType.COLLATE, 248 TokenType.COMMAND, 249 TokenType.COMMENT, 250 TokenType.COMMIT, 251 TokenType.CONSTRAINT, 252 TokenType.DEFAULT, 253 TokenType.DELETE, 254 TokenType.DESC, 255 TokenType.DESCRIBE, 256 TokenType.DICTIONARY, 257 TokenType.DIV, 258 TokenType.END, 259 TokenType.EXECUTE, 260 TokenType.ESCAPE, 261 TokenType.FALSE, 262 TokenType.FIRST, 263 TokenType.FILTER, 264 TokenType.FORMAT, 265 TokenType.FULL, 266 TokenType.IS, 267 TokenType.ISNULL, 268 TokenType.INTERVAL, 269 TokenType.KEEP, 270 TokenType.LEFT, 271 TokenType.LOAD, 272 TokenType.MERGE, 273 TokenType.NATURAL, 274 TokenType.NEXT, 275 TokenType.OFFSET, 276 TokenType.ORDINALITY, 277 TokenType.OVERWRITE, 278 TokenType.PARTITION, 279 TokenType.PERCENT, 280 TokenType.PIVOT, 281 TokenType.PRAGMA, 282 TokenType.RANGE, 283 TokenType.REFERENCES, 284 TokenType.RIGHT, 285 TokenType.ROW, 286 TokenType.ROWS, 287 TokenType.SEMI, 288 TokenType.SET, 289 TokenType.SETTINGS, 290 TokenType.SHOW, 291 TokenType.TEMPORARY, 292 TokenType.TOP, 293 TokenType.TRUE, 294 TokenType.UNIQUE, 295 TokenType.UNPIVOT, 296 TokenType.UPDATE, 297 TokenType.VOLATILE, 298 TokenType.WINDOW, 299 *CREATABLES, 300 *SUBQUERY_PREDICATES, 301 *TYPE_TOKENS, 302 *NO_PAREN_FUNCTIONS, 303 } 304 305 INTERVAL_VARS = ID_VAR_TOKENS - {TokenType.END} 306 307 TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - { 308 TokenType.APPLY, 309 TokenType.ASOF, 310 TokenType.FULL, 311 TokenType.LEFT, 312 TokenType.LOCK, 313 TokenType.NATURAL, 314 TokenType.OFFSET, 315 TokenType.RIGHT, 316 TokenType.WINDOW, 317 } 318 319 COMMENT_TABLE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.IS} 320 321 UPDATE_ALIAS_TOKENS = TABLE_ALIAS_TOKENS - {TokenType.SET} 322 323 TRIM_TYPES = {"LEADING", "TRAILING", "BOTH"} 324 325 FUNC_TOKENS = { 326 TokenType.COMMAND, 327 TokenType.CURRENT_DATE, 328 TokenType.CURRENT_DATETIME, 329 TokenType.CURRENT_TIMESTAMP, 330 TokenType.CURRENT_TIME, 331 TokenType.CURRENT_USER, 332 TokenType.FILTER, 333 TokenType.FIRST, 334 TokenType.FORMAT, 335 TokenType.GLOB, 336 TokenType.IDENTIFIER, 337 TokenType.INDEX, 338 TokenType.ISNULL, 339 TokenType.ILIKE, 340 TokenType.INSERT, 341 TokenType.LIKE, 342 TokenType.MERGE, 343 TokenType.OFFSET, 344 TokenType.PRIMARY_KEY, 345 TokenType.RANGE, 346 TokenType.REPLACE, 347 TokenType.RLIKE, 348 TokenType.ROW, 349 TokenType.UNNEST, 350 TokenType.VAR, 351 TokenType.LEFT, 352 TokenType.RIGHT, 353 TokenType.DATE, 354 TokenType.DATETIME, 355 TokenType.TABLE, 356 TokenType.TIMESTAMP, 357 TokenType.TIMESTAMPTZ, 358 TokenType.WINDOW, 359 TokenType.XOR, 360 *TYPE_TOKENS, 361 *SUBQUERY_PREDICATES, 362 } 363 364 CONJUNCTION = { 365 TokenType.AND: exp.And, 366 TokenType.OR: exp.Or, 367 } 368 369 EQUALITY = { 370 TokenType.EQ: exp.EQ, 371 TokenType.NEQ: exp.NEQ, 372 TokenType.NULLSAFE_EQ: exp.NullSafeEQ, 373 } 374 375 COMPARISON = { 376 TokenType.GT: exp.GT, 377 TokenType.GTE: exp.GTE, 378 TokenType.LT: exp.LT, 379 TokenType.LTE: exp.LTE, 380 } 381 382 BITWISE = { 383 TokenType.AMP: exp.BitwiseAnd, 384 TokenType.CARET: exp.BitwiseXor, 385 TokenType.PIPE: exp.BitwiseOr, 386 TokenType.DPIPE: exp.DPipe, 387 } 388 389 TERM = { 390 TokenType.DASH: exp.Sub, 391 TokenType.PLUS: exp.Add, 392 TokenType.MOD: exp.Mod, 393 TokenType.COLLATE: exp.Collate, 394 } 395 396 FACTOR = { 397 TokenType.DIV: exp.IntDiv, 398 TokenType.LR_ARROW: exp.Distance, 399 TokenType.SLASH: exp.Div, 400 TokenType.STAR: exp.Mul, 401 } 402 403 TIMES = { 404 TokenType.TIME, 405 TokenType.TIMETZ, 406 } 407 408 TIMESTAMPS = { 409 TokenType.TIMESTAMP, 410 TokenType.TIMESTAMPTZ, 411 TokenType.TIMESTAMPLTZ, 412 *TIMES, 413 } 414 415 SET_OPERATIONS = { 416 TokenType.UNION, 417 TokenType.INTERSECT, 418 TokenType.EXCEPT, 419 } 420 421 JOIN_METHODS = { 422 TokenType.NATURAL, 423 TokenType.ASOF, 424 } 425 426 JOIN_SIDES = { 427 TokenType.LEFT, 428 TokenType.RIGHT, 429 TokenType.FULL, 430 } 431 432 JOIN_KINDS = { 433 TokenType.INNER, 434 TokenType.OUTER, 435 TokenType.CROSS, 436 TokenType.SEMI, 437 TokenType.ANTI, 438 } 439 440 JOIN_HINTS: t.Set[str] = set() 441 442 LAMBDAS = { 443 TokenType.ARROW: lambda self, expressions: self.expression( 444 exp.Lambda, 445 this=self._replace_lambda( 446 self._parse_conjunction(), 447 {node.name for node in expressions}, 448 ), 449 expressions=expressions, 450 ), 451 TokenType.FARROW: lambda self, expressions: self.expression( 452 exp.Kwarg, 453 this=exp.var(expressions[0].name), 454 expression=self._parse_conjunction(), 455 ), 456 } 457 458 COLUMN_OPERATORS = { 459 TokenType.DOT: None, 460 TokenType.DCOLON: lambda self, this, to: self.expression( 461 exp.Cast if self.STRICT_CAST else exp.TryCast, 462 this=this, 463 to=to, 464 ), 465 TokenType.ARROW: lambda self, this, path: self.expression( 466 exp.JSONExtract, 467 this=this, 468 expression=path, 469 ), 470 TokenType.DARROW: lambda self, this, path: self.expression( 471 exp.JSONExtractScalar, 472 this=this, 473 expression=path, 474 ), 475 TokenType.HASH_ARROW: lambda self, this, path: self.expression( 476 exp.JSONBExtract, 477 this=this, 478 expression=path, 479 ), 480 TokenType.DHASH_ARROW: lambda self, this, path: self.expression( 481 exp.JSONBExtractScalar, 482 this=this, 483 expression=path, 484 ), 485 TokenType.PLACEHOLDER: lambda self, this, key: self.expression( 486 exp.JSONBContains, 487 this=this, 488 expression=key, 489 ), 490 } 491 492 EXPRESSION_PARSERS = { 493 exp.Cluster: lambda self: self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY), 494 exp.Column: lambda self: self._parse_column(), 495 exp.Condition: lambda self: self._parse_conjunction(), 496 exp.DataType: lambda self: self._parse_types(allow_identifiers=False), 497 exp.Expression: lambda self: self._parse_statement(), 498 exp.From: lambda self: self._parse_from(), 499 exp.Group: lambda self: self._parse_group(), 500 exp.Having: lambda self: self._parse_having(), 501 exp.Identifier: lambda self: self._parse_id_var(), 502 exp.Join: lambda self: self._parse_join(), 503 exp.Lambda: lambda self: self._parse_lambda(), 504 exp.Lateral: lambda self: self._parse_lateral(), 505 exp.Limit: lambda self: self._parse_limit(), 506 exp.Offset: lambda self: self._parse_offset(), 507 exp.Order: lambda self: self._parse_order(), 508 exp.Ordered: lambda self: self._parse_ordered(), 509 exp.Properties: lambda self: self._parse_properties(), 510 exp.Qualify: lambda self: self._parse_qualify(), 511 exp.Returning: lambda self: self._parse_returning(), 512 exp.Sort: lambda self: self._parse_sort(exp.Sort, TokenType.SORT_BY), 513 exp.Table: lambda self: self._parse_table_parts(), 514 exp.TableAlias: lambda self: self._parse_table_alias(), 515 exp.Where: lambda self: self._parse_where(), 516 exp.Window: lambda self: self._parse_named_window(), 517 exp.With: lambda self: self._parse_with(), 518 "JOIN_TYPE": lambda self: self._parse_join_parts(), 519 } 520 521 STATEMENT_PARSERS = { 522 TokenType.ALTER: lambda self: self._parse_alter(), 523 TokenType.BEGIN: lambda self: self._parse_transaction(), 524 TokenType.CACHE: lambda self: self._parse_cache(), 525 TokenType.COMMIT: lambda self: self._parse_commit_or_rollback(), 526 TokenType.COMMENT: lambda self: self._parse_comment(), 527 TokenType.CREATE: lambda self: self._parse_create(), 528 TokenType.DELETE: lambda self: self._parse_delete(), 529 TokenType.DESC: lambda self: self._parse_describe(), 530 TokenType.DESCRIBE: lambda self: self._parse_describe(), 531 TokenType.DROP: lambda self: self._parse_drop(), 532 TokenType.INSERT: lambda self: self._parse_insert(), 533 TokenType.LOAD: lambda self: self._parse_load(), 534 TokenType.MERGE: lambda self: self._parse_merge(), 535 TokenType.PIVOT: lambda self: self._parse_simplified_pivot(), 536 TokenType.PRAGMA: lambda self: self.expression(exp.Pragma, this=self._parse_expression()), 537 TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(), 538 TokenType.SET: lambda self: self._parse_set(), 539 TokenType.UNCACHE: lambda self: self._parse_uncache(), 540 TokenType.UPDATE: lambda self: self._parse_update(), 541 TokenType.USE: lambda self: self.expression( 542 exp.Use, 543 kind=self._match_texts(("ROLE", "WAREHOUSE", "DATABASE", "SCHEMA")) 544 and exp.var(self._prev.text), 545 this=self._parse_table(schema=False), 546 ), 547 } 548 549 UNARY_PARSERS = { 550 TokenType.PLUS: lambda self: self._parse_unary(), # Unary + is handled as a no-op 551 TokenType.NOT: lambda self: self.expression(exp.Not, this=self._parse_equality()), 552 TokenType.TILDA: lambda self: self.expression(exp.BitwiseNot, this=self._parse_unary()), 553 TokenType.DASH: lambda self: self.expression(exp.Neg, this=self._parse_unary()), 554 } 555 556 PRIMARY_PARSERS = { 557 TokenType.STRING: lambda self, token: self.expression( 558 exp.Literal, this=token.text, is_string=True 559 ), 560 TokenType.NUMBER: lambda self, token: self.expression( 561 exp.Literal, this=token.text, is_string=False 562 ), 563 TokenType.STAR: lambda self, _: self.expression( 564 exp.Star, **{"except": self._parse_except(), "replace": self._parse_replace()} 565 ), 566 TokenType.NULL: lambda self, _: self.expression(exp.Null), 567 TokenType.TRUE: lambda self, _: self.expression(exp.Boolean, this=True), 568 TokenType.FALSE: lambda self, _: self.expression(exp.Boolean, this=False), 569 TokenType.BIT_STRING: lambda self, token: self.expression(exp.BitString, this=token.text), 570 TokenType.HEX_STRING: lambda self, token: self.expression(exp.HexString, this=token.text), 571 TokenType.BYTE_STRING: lambda self, token: self.expression(exp.ByteString, this=token.text), 572 TokenType.INTRODUCER: lambda self, token: self._parse_introducer(token), 573 TokenType.NATIONAL_STRING: lambda self, token: self.expression( 574 exp.National, this=token.text 575 ), 576 TokenType.RAW_STRING: lambda self, token: self.expression(exp.RawString, this=token.text), 577 TokenType.SESSION_PARAMETER: lambda self, _: self._parse_session_parameter(), 578 } 579 580 PLACEHOLDER_PARSERS = { 581 TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder), 582 TokenType.PARAMETER: lambda self: self._parse_parameter(), 583 TokenType.COLON: lambda self: self.expression(exp.Placeholder, this=self._prev.text) 584 if self._match(TokenType.NUMBER) or self._match_set(self.ID_VAR_TOKENS) 585 else None, 586 } 587 588 RANGE_PARSERS = { 589 TokenType.BETWEEN: lambda self, this: self._parse_between(this), 590 TokenType.GLOB: binary_range_parser(exp.Glob), 591 TokenType.ILIKE: binary_range_parser(exp.ILike), 592 TokenType.IN: lambda self, this: self._parse_in(this), 593 TokenType.IRLIKE: binary_range_parser(exp.RegexpILike), 594 TokenType.IS: lambda self, this: self._parse_is(this), 595 TokenType.LIKE: binary_range_parser(exp.Like), 596 TokenType.OVERLAPS: binary_range_parser(exp.Overlaps), 597 TokenType.RLIKE: binary_range_parser(exp.RegexpLike), 598 TokenType.SIMILAR_TO: binary_range_parser(exp.SimilarTo), 599 TokenType.FOR: lambda self, this: self._parse_comprehension(this), 600 } 601 602 PROPERTY_PARSERS: t.Dict[str, t.Callable] = { 603 "ALGORITHM": lambda self: self._parse_property_assignment(exp.AlgorithmProperty), 604 "AUTO_INCREMENT": lambda self: self._parse_property_assignment(exp.AutoIncrementProperty), 605 "BLOCKCOMPRESSION": lambda self: self._parse_blockcompression(), 606 "CHARACTER SET": lambda self: self._parse_character_set(), 607 "CHECKSUM": lambda self: self._parse_checksum(), 608 "CLUSTER BY": lambda self: self._parse_cluster(), 609 "CLUSTERED": lambda self: self._parse_clustered_by(), 610 "COLLATE": lambda self: self._parse_property_assignment(exp.CollateProperty), 611 "COMMENT": lambda self: self._parse_property_assignment(exp.SchemaCommentProperty), 612 "COPY": lambda self: self._parse_copy_property(), 613 "DATABLOCKSIZE": lambda self, **kwargs: self._parse_datablocksize(**kwargs), 614 "DEFINER": lambda self: self._parse_definer(), 615 "DETERMINISTIC": lambda self: self.expression( 616 exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE") 617 ), 618 "DISTKEY": lambda self: self._parse_distkey(), 619 "DISTSTYLE": lambda self: self._parse_property_assignment(exp.DistStyleProperty), 620 "ENGINE": lambda self: self._parse_property_assignment(exp.EngineProperty), 621 "EXECUTE": lambda self: self._parse_property_assignment(exp.ExecuteAsProperty), 622 "EXTERNAL": lambda self: self.expression(exp.ExternalProperty), 623 "FALLBACK": lambda self, **kwargs: self._parse_fallback(**kwargs), 624 "FORMAT": lambda self: self._parse_property_assignment(exp.FileFormatProperty), 625 "FREESPACE": lambda self: self._parse_freespace(), 626 "HEAP": lambda self: self.expression(exp.HeapProperty), 627 "IMMUTABLE": lambda self: self.expression( 628 exp.StabilityProperty, this=exp.Literal.string("IMMUTABLE") 629 ), 630 "JOURNAL": lambda self, **kwargs: self._parse_journal(**kwargs), 631 "LANGUAGE": lambda self: self._parse_property_assignment(exp.LanguageProperty), 632 "LAYOUT": lambda self: self._parse_dict_property(this="LAYOUT"), 633 "LIFETIME": lambda self: self._parse_dict_range(this="LIFETIME"), 634 "LIKE": lambda self: self._parse_create_like(), 635 "LOCATION": lambda self: self._parse_property_assignment(exp.LocationProperty), 636 "LOCK": lambda self: self._parse_locking(), 637 "LOCKING": lambda self: self._parse_locking(), 638 "LOG": lambda self, **kwargs: self._parse_log(**kwargs), 639 "MATERIALIZED": lambda self: self.expression(exp.MaterializedProperty), 640 "MERGEBLOCKRATIO": lambda self, **kwargs: self._parse_mergeblockratio(**kwargs), 641 "MULTISET": lambda self: self.expression(exp.SetProperty, multi=True), 642 "NO": lambda self: self._parse_no_property(), 643 "ON": lambda self: self._parse_on_property(), 644 "ORDER BY": lambda self: self._parse_order(skip_order_token=True), 645 "PARTITION BY": lambda self: self._parse_partitioned_by(), 646 "PARTITIONED BY": lambda self: self._parse_partitioned_by(), 647 "PARTITIONED_BY": lambda self: self._parse_partitioned_by(), 648 "PRIMARY KEY": lambda self: self._parse_primary_key(in_props=True), 649 "RANGE": lambda self: self._parse_dict_range(this="RANGE"), 650 "RETURNS": lambda self: self._parse_returns(), 651 "ROW": lambda self: self._parse_row(), 652 "ROW_FORMAT": lambda self: self._parse_property_assignment(exp.RowFormatProperty), 653 "SET": lambda self: self.expression(exp.SetProperty, multi=False), 654 "SETTINGS": lambda self: self.expression( 655 exp.SettingsProperty, expressions=self._parse_csv(self._parse_set_item) 656 ), 657 "SORTKEY": lambda self: self._parse_sortkey(), 658 "SOURCE": lambda self: self._parse_dict_property(this="SOURCE"), 659 "STABLE": lambda self: self.expression( 660 exp.StabilityProperty, this=exp.Literal.string("STABLE") 661 ), 662 "STORED": lambda self: self._parse_stored(), 663 "TBLPROPERTIES": lambda self: self._parse_wrapped_csv(self._parse_property), 664 "TEMP": lambda self: self.expression(exp.TemporaryProperty), 665 "TEMPORARY": lambda self: self.expression(exp.TemporaryProperty), 666 "TO": lambda self: self._parse_to_table(), 667 "TRANSIENT": lambda self: self.expression(exp.TransientProperty), 668 "TTL": lambda self: self._parse_ttl(), 669 "USING": lambda self: self._parse_property_assignment(exp.FileFormatProperty), 670 "VOLATILE": lambda self: self._parse_volatile_property(), 671 "WITH": lambda self: self._parse_with_property(), 672 } 673 674 CONSTRAINT_PARSERS = { 675 "AUTOINCREMENT": lambda self: self._parse_auto_increment(), 676 "AUTO_INCREMENT": lambda self: self._parse_auto_increment(), 677 "CASESPECIFIC": lambda self: self.expression(exp.CaseSpecificColumnConstraint, not_=False), 678 "CHARACTER SET": lambda self: self.expression( 679 exp.CharacterSetColumnConstraint, this=self._parse_var_or_string() 680 ), 681 "CHECK": lambda self: self.expression( 682 exp.CheckColumnConstraint, this=self._parse_wrapped(self._parse_conjunction) 683 ), 684 "COLLATE": lambda self: self.expression( 685 exp.CollateColumnConstraint, this=self._parse_var() 686 ), 687 "COMMENT": lambda self: self.expression( 688 exp.CommentColumnConstraint, this=self._parse_string() 689 ), 690 "COMPRESS": lambda self: self._parse_compress(), 691 "CLUSTERED": lambda self: self.expression( 692 exp.ClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered) 693 ), 694 "NONCLUSTERED": lambda self: self.expression( 695 exp.NonClusteredColumnConstraint, this=self._parse_wrapped_csv(self._parse_ordered) 696 ), 697 "DEFAULT": lambda self: self.expression( 698 exp.DefaultColumnConstraint, this=self._parse_bitwise() 699 ), 700 "ENCODE": lambda self: self.expression(exp.EncodeColumnConstraint, this=self._parse_var()), 701 "FOREIGN KEY": lambda self: self._parse_foreign_key(), 702 "FORMAT": lambda self: self.expression( 703 exp.DateFormatColumnConstraint, this=self._parse_var_or_string() 704 ), 705 "GENERATED": lambda self: self._parse_generated_as_identity(), 706 "IDENTITY": lambda self: self._parse_auto_increment(), 707 "INLINE": lambda self: self._parse_inline(), 708 "LIKE": lambda self: self._parse_create_like(), 709 "NOT": lambda self: self._parse_not_constraint(), 710 "NULL": lambda self: self.expression(exp.NotNullColumnConstraint, allow_null=True), 711 "ON": lambda self: ( 712 self._match(TokenType.UPDATE) 713 and self.expression(exp.OnUpdateColumnConstraint, this=self._parse_function()) 714 ) 715 or self.expression(exp.OnProperty, this=self._parse_id_var()), 716 "PATH": lambda self: self.expression(exp.PathColumnConstraint, this=self._parse_string()), 717 "PRIMARY KEY": lambda self: self._parse_primary_key(), 718 "REFERENCES": lambda self: self._parse_references(match=False), 719 "TITLE": lambda self: self.expression( 720 exp.TitleColumnConstraint, this=self._parse_var_or_string() 721 ), 722 "TTL": lambda self: self.expression(exp.MergeTreeTTL, expressions=[self._parse_bitwise()]), 723 "UNIQUE": lambda self: self._parse_unique(), 724 "UPPERCASE": lambda self: self.expression(exp.UppercaseColumnConstraint), 725 "WITH": lambda self: self.expression( 726 exp.Properties, expressions=self._parse_wrapped_csv(self._parse_property) 727 ), 728 } 729 730 ALTER_PARSERS = { 731 "ADD": lambda self: self._parse_alter_table_add(), 732 "ALTER": lambda self: self._parse_alter_table_alter(), 733 "DELETE": lambda self: self.expression(exp.Delete, where=self._parse_where()), 734 "DROP": lambda self: self._parse_alter_table_drop(), 735 "RENAME": lambda self: self._parse_alter_table_rename(), 736 } 737 738 SCHEMA_UNNAMED_CONSTRAINTS = {"CHECK", "FOREIGN KEY", "LIKE", "PRIMARY KEY", "UNIQUE"} 739 740 NO_PAREN_FUNCTION_PARSERS = { 741 "ANY": lambda self: self.expression(exp.Any, this=self._parse_bitwise()), 742 "CASE": lambda self: self._parse_case(), 743 "IF": lambda self: self._parse_if(), 744 "NEXT": lambda self: self._parse_next_value_for(), 745 } 746 747 INVALID_FUNC_NAME_TOKENS = { 748 TokenType.IDENTIFIER, 749 TokenType.STRING, 750 } 751 752 FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"} 753 754 FUNCTION_PARSERS = { 755 "ANY_VALUE": lambda self: self._parse_any_value(), 756 "CAST": lambda self: self._parse_cast(self.STRICT_CAST), 757 "CONCAT": lambda self: self._parse_concat(), 758 "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST), 759 "DECODE": lambda self: self._parse_decode(), 760 "EXTRACT": lambda self: self._parse_extract(), 761 "JSON_OBJECT": lambda self: self._parse_json_object(), 762 "LOG": lambda self: self._parse_logarithm(), 763 "MATCH": lambda self: self._parse_match_against(), 764 "OPENJSON": lambda self: self._parse_open_json(), 765 "POSITION": lambda self: self._parse_position(), 766 "SAFE_CAST": lambda self: self._parse_cast(False), 767 "STRING_AGG": lambda self: self._parse_string_agg(), 768 "SUBSTRING": lambda self: self._parse_substring(), 769 "TRIM": lambda self: self._parse_trim(), 770 "TRY_CAST": lambda self: self._parse_cast(False), 771 "TRY_CONVERT": lambda self: self._parse_convert(False), 772 } 773 774 QUERY_MODIFIER_PARSERS = { 775 TokenType.MATCH_RECOGNIZE: lambda self: ("match", self._parse_match_recognize()), 776 TokenType.WHERE: lambda self: ("where", self._parse_where()), 777 TokenType.GROUP_BY: lambda self: ("group", self._parse_group()), 778 TokenType.HAVING: lambda self: ("having", self._parse_having()), 779 TokenType.QUALIFY: lambda self: ("qualify", self._parse_qualify()), 780 TokenType.WINDOW: lambda self: ("windows", self._parse_window_clause()), 781 TokenType.ORDER_BY: lambda self: ("order", self._parse_order()), 782 TokenType.LIMIT: lambda self: ("limit", self._parse_limit()), 783 TokenType.FETCH: lambda self: ("limit", self._parse_limit()), 784 TokenType.OFFSET: lambda self: ("offset", self._parse_offset()), 785 TokenType.FOR: lambda self: ("locks", self._parse_locks()), 786 TokenType.LOCK: lambda self: ("locks", self._parse_locks()), 787 TokenType.TABLE_SAMPLE: lambda self: ("sample", self._parse_table_sample(as_modifier=True)), 788 TokenType.USING: lambda self: ("sample", self._parse_table_sample(as_modifier=True)), 789 TokenType.CLUSTER_BY: lambda self: ( 790 "cluster", 791 self._parse_sort(exp.Cluster, TokenType.CLUSTER_BY), 792 ), 793 TokenType.DISTRIBUTE_BY: lambda self: ( 794 "distribute", 795 self._parse_sort(exp.Distribute, TokenType.DISTRIBUTE_BY), 796 ), 797 TokenType.SORT_BY: lambda self: ("sort", self._parse_sort(exp.Sort, TokenType.SORT_BY)), 798 TokenType.CONNECT_BY: lambda self: ("connect", self._parse_connect(skip_start_token=True)), 799 TokenType.START_WITH: lambda self: ("connect", self._parse_connect()), 800 } 801 802 SET_PARSERS = { 803 "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"), 804 "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"), 805 "SESSION": lambda self: self._parse_set_item_assignment("SESSION"), 806 "TRANSACTION": lambda self: self._parse_set_transaction(), 807 } 808 809 SHOW_PARSERS: t.Dict[str, t.Callable] = {} 810 811 TYPE_LITERAL_PARSERS: t.Dict[exp.DataType.Type, t.Callable] = {} 812 813 MODIFIABLES = (exp.Subquery, exp.Subqueryable, exp.Table) 814 815 DDL_SELECT_TOKENS = {TokenType.SELECT, TokenType.WITH, TokenType.L_PAREN} 816 817 PRE_VOLATILE_TOKENS = {TokenType.CREATE, TokenType.REPLACE, TokenType.UNIQUE} 818 819 TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"} 820 TRANSACTION_CHARACTERISTICS = { 821 "ISOLATION LEVEL REPEATABLE READ", 822 "ISOLATION LEVEL READ COMMITTED", 823 "ISOLATION LEVEL READ UNCOMMITTED", 824 "ISOLATION LEVEL SERIALIZABLE", 825 "READ WRITE", 826 "READ ONLY", 827 } 828 829 INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"} 830 831 CLONE_KINDS = {"TIMESTAMP", "OFFSET", "STATEMENT"} 832 833 TABLE_INDEX_HINT_TOKENS = {TokenType.FORCE, TokenType.IGNORE, TokenType.USE} 834 835 WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS} 836 WINDOW_BEFORE_PAREN_TOKENS = {TokenType.OVER} 837 WINDOW_SIDES = {"FOLLOWING", "PRECEDING"} 838 839 ADD_CONSTRAINT_TOKENS = {TokenType.CONSTRAINT, TokenType.PRIMARY_KEY, TokenType.FOREIGN_KEY} 840 841 DISTINCT_TOKENS = {TokenType.DISTINCT} 842 843 STRICT_CAST = True 844 845 # A NULL arg in CONCAT yields NULL by default 846 CONCAT_NULL_OUTPUTS_STRING = False 847 848 PREFIXED_PIVOT_COLUMNS = False 849 IDENTIFY_PIVOT_STRINGS = False 850 851 LOG_BASE_FIRST = True 852 LOG_DEFAULTS_TO_LN = False 853 854 SUPPORTS_USER_DEFINED_TYPES = True 855 856 # Whether or not ADD is present for each column added by ALTER TABLE 857 ALTER_TABLE_ADD_COLUMN_KEYWORD = True 858 859 __slots__ = ( 860 "error_level", 861 "error_message_context", 862 "max_errors", 863 "sql", 864 "errors", 865 "_tokens", 866 "_index", 867 "_curr", 868 "_next", 869 "_prev", 870 "_prev_comments", 871 "_tokenizer", 872 ) 873 874 # Autofilled 875 TOKENIZER_CLASS: t.Type[Tokenizer] = Tokenizer 876 INDEX_OFFSET: int = 0 877 UNNEST_COLUMN_ONLY: bool = False 878 ALIAS_POST_TABLESAMPLE: bool = False 879 STRICT_STRING_CONCAT = False 880 NORMALIZE_FUNCTIONS = "upper" 881 NULL_ORDERING: str = "nulls_are_small" 882 SHOW_TRIE: t.Dict = {} 883 SET_TRIE: t.Dict = {} 884 FORMAT_MAPPING: t.Dict[str, str] = {} 885 FORMAT_TRIE: t.Dict = {} 886 TIME_MAPPING: t.Dict[str, str] = {} 887 TIME_TRIE: t.Dict = {} 888 889 def __init__( 890 self, 891 error_level: t.Optional[ErrorLevel] = None, 892 error_message_context: int = 100, 893 max_errors: int = 3, 894 ): 895 self.error_level = error_level or ErrorLevel.IMMEDIATE 896 self.error_message_context = error_message_context 897 self.max_errors = max_errors 898 self._tokenizer = self.TOKENIZER_CLASS() 899 self.reset() 900 901 def reset(self): 902 self.sql = "" 903 self.errors = [] 904 self._tokens = [] 905 self._index = 0 906 self._curr = None 907 self._next = None 908 self._prev = None 909 self._prev_comments = None 910 911 def parse( 912 self, raw_tokens: t.List[Token], sql: t.Optional[str] = None 913 ) -> t.List[t.Optional[exp.Expression]]: 914 """ 915 Parses a list of tokens and returns a list of syntax trees, one tree 916 per parsed SQL statement. 917 918 Args: 919 raw_tokens: The list of tokens. 920 sql: The original SQL string, used to produce helpful debug messages. 921 922 Returns: 923 The list of the produced syntax trees. 924 """ 925 return self._parse( 926 parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql 927 ) 928 929 def parse_into( 930 self, 931 expression_types: exp.IntoType, 932 raw_tokens: t.List[Token], 933 sql: t.Optional[str] = None, 934 ) -> t.List[t.Optional[exp.Expression]]: 935 """ 936 Parses a list of tokens into a given Expression type. If a collection of Expression 937 types is given instead, this method will try to parse the token list into each one 938 of them, stopping at the first for which the parsing succeeds. 939 940 Args: 941 expression_types: The expression type(s) to try and parse the token list into. 942 raw_tokens: The list of tokens. 943 sql: The original SQL string, used to produce helpful debug messages. 944 945 Returns: 946 The target Expression. 947 """ 948 errors = [] 949 for expression_type in ensure_list(expression_types): 950 parser = self.EXPRESSION_PARSERS.get(expression_type) 951 if not parser: 952 raise TypeError(f"No parser registered for {expression_type}") 953 954 try: 955 return self._parse(parser, raw_tokens, sql) 956 except ParseError as e: 957 e.errors[0]["into_expression"] = expression_type 958 errors.append(e) 959 960 raise ParseError( 961 f"Failed to parse '{sql or raw_tokens}' into {expression_types}", 962 errors=merge_errors(errors), 963 ) from errors[-1] 964 965 def _parse( 966 self, 967 parse_method: t.Callable[[Parser], t.Optional[exp.Expression]], 968 raw_tokens: t.List[Token], 969 sql: t.Optional[str] = None, 970 ) -> t.List[t.Optional[exp.Expression]]: 971 self.reset() 972 self.sql = sql or "" 973 974 total = len(raw_tokens) 975 chunks: t.List[t.List[Token]] = [[]] 976 977 for i, token in enumerate(raw_tokens): 978 if token.token_type == TokenType.SEMICOLON: 979 if i < total - 1: 980 chunks.append([]) 981 else: 982 chunks[-1].append(token) 983 984 expressions = [] 985 986 for tokens in chunks: 987 self._index = -1 988 self._tokens = tokens 989 self._advance() 990 991 expressions.append(parse_method(self)) 992 993 if self._index < len(self._tokens): 994 self.raise_error("Invalid expression / Unexpected token") 995 996 self.check_errors() 997 998 return expressions 999 1000 def check_errors(self) -> None: 1001 """Logs or raises any found errors, depending on the chosen error level setting.""" 1002 if self.error_level == ErrorLevel.WARN: 1003 for error in self.errors: 1004 logger.error(str(error)) 1005 elif self.error_level == ErrorLevel.RAISE and self.errors: 1006 raise ParseError( 1007 concat_messages(self.errors, self.max_errors), 1008 errors=merge_errors(self.errors), 1009 ) 1010 1011 def raise_error(self, message: str, token: t.Optional[Token] = None) -> None: 1012 """ 1013 Appends an error in the list of recorded errors or raises it, depending on the chosen 1014 error level setting. 1015 """ 1016 token = token or self._curr or self._prev or Token.string("") 1017 start = token.start 1018 end = token.end + 1 1019 start_context = self.sql[max(start - self.error_message_context, 0) : start] 1020 highlight = self.sql[start:end] 1021 end_context = self.sql[end : end + self.error_message_context] 1022 1023 error = ParseError.new( 1024 f"{message}. Line {token.line}, Col: {token.col}.\n" 1025 f" {start_context}\033[4m{highlight}\033[0m{end_context}", 1026 description=message, 1027 line=token.line, 1028 col=token.col, 1029 start_context=start_context, 1030 highlight=highlight, 1031 end_context=end_context, 1032 ) 1033 1034 if self.error_level == ErrorLevel.IMMEDIATE: 1035 raise error 1036 1037 self.errors.append(error) 1038 1039 def expression( 1040 self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs 1041 ) -> E: 1042 """ 1043 Creates a new, validated Expression. 1044 1045 Args: 1046 exp_class: The expression class to instantiate. 1047 comments: An optional list of comments to attach to the expression. 1048 kwargs: The arguments to set for the expression along with their respective values. 1049 1050 Returns: 1051 The target expression. 1052 """ 1053 instance = exp_class(**kwargs) 1054 instance.add_comments(comments) if comments else self._add_comments(instance) 1055 return self.validate_expression(instance) 1056 1057 def _add_comments(self, expression: t.Optional[exp.Expression]) -> None: 1058 if expression and self._prev_comments: 1059 expression.add_comments(self._prev_comments) 1060 self._prev_comments = None 1061 1062 def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E: 1063 """ 1064 Validates an Expression, making sure that all its mandatory arguments are set. 1065 1066 Args: 1067 expression: The expression to validate. 1068 args: An optional list of items that was used to instantiate the expression, if it's a Func. 1069 1070 Returns: 1071 The validated expression. 1072 """ 1073 if self.error_level != ErrorLevel.IGNORE: 1074 for error_message in expression.error_messages(args): 1075 self.raise_error(error_message) 1076 1077 return expression 1078 1079 def _find_sql(self, start: Token, end: Token) -> str: 1080 return self.sql[start.start : end.end + 1] 1081 1082 def _advance(self, times: int = 1) -> None: 1083 self._index += times 1084 self._curr = seq_get(self._tokens, self._index) 1085 self._next = seq_get(self._tokens, self._index + 1) 1086 1087 if self._index > 0: 1088 self._prev = self._tokens[self._index - 1] 1089 self._prev_comments = self._prev.comments 1090 else: 1091 self._prev = None 1092 self._prev_comments = None 1093 1094 def _retreat(self, index: int) -> None: 1095 if index != self._index: 1096 self._advance(index - self._index) 1097 1098 def _parse_command(self) -> exp.Command: 1099 return self.expression(exp.Command, this=self._prev.text, expression=self._parse_string()) 1100 1101 def _parse_comment(self, allow_exists: bool = True) -> exp.Expression: 1102 start = self._prev 1103 exists = self._parse_exists() if allow_exists else None 1104 1105 self._match(TokenType.ON) 1106 1107 kind = self._match_set(self.CREATABLES) and self._prev 1108 if not kind: 1109 return self._parse_as_command(start) 1110 1111 if kind.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE): 1112 this = self._parse_user_defined_function(kind=kind.token_type) 1113 elif kind.token_type == TokenType.TABLE: 1114 this = self._parse_table(alias_tokens=self.COMMENT_TABLE_ALIAS_TOKENS) 1115 elif kind.token_type == TokenType.COLUMN: 1116 this = self._parse_column() 1117 else: 1118 this = self._parse_id_var() 1119 1120 self._match(TokenType.IS) 1121 1122 return self.expression( 1123 exp.Comment, this=this, kind=kind.text, expression=self._parse_string(), exists=exists 1124 ) 1125 1126 def _parse_to_table( 1127 self, 1128 ) -> exp.ToTableProperty: 1129 table = self._parse_table_parts(schema=True) 1130 return self.expression(exp.ToTableProperty, this=table) 1131 1132 # https://clickhouse.com/docs/en/engines/table-engines/mergetree-family/mergetree#mergetree-table-ttl 1133 def _parse_ttl(self) -> exp.Expression: 1134 def _parse_ttl_action() -> t.Optional[exp.Expression]: 1135 this = self._parse_bitwise() 1136 1137 if self._match_text_seq("DELETE"): 1138 return self.expression(exp.MergeTreeTTLAction, this=this, delete=True) 1139 if self._match_text_seq("RECOMPRESS"): 1140 return self.expression( 1141 exp.MergeTreeTTLAction, this=this, recompress=self._parse_bitwise() 1142 ) 1143 if self._match_text_seq("TO", "DISK"): 1144 return self.expression( 1145 exp.MergeTreeTTLAction, this=this, to_disk=self._parse_string() 1146 ) 1147 if self._match_text_seq("TO", "VOLUME"): 1148 return self.expression( 1149 exp.MergeTreeTTLAction, this=this, to_volume=self._parse_string() 1150 ) 1151 1152 return this 1153 1154 expressions = self._parse_csv(_parse_ttl_action) 1155 where = self._parse_where() 1156 group = self._parse_group() 1157 1158 aggregates = None 1159 if group and self._match(TokenType.SET): 1160 aggregates = self._parse_csv(self._parse_set_item) 1161 1162 return self.expression( 1163 exp.MergeTreeTTL, 1164 expressions=expressions, 1165 where=where, 1166 group=group, 1167 aggregates=aggregates, 1168 ) 1169 1170 def _parse_statement(self) -> t.Optional[exp.Expression]: 1171 if self._curr is None: 1172 return None 1173 1174 if self._match_set(self.STATEMENT_PARSERS): 1175 return self.STATEMENT_PARSERS[self._prev.token_type](self) 1176 1177 if self._match_set(Tokenizer.COMMANDS): 1178 return self._parse_command() 1179 1180 expression = self._parse_expression() 1181 expression = self._parse_set_operations(expression) if expression else self._parse_select() 1182 return self._parse_query_modifiers(expression) 1183 1184 def _parse_drop(self, exists: bool = False) -> exp.Drop | exp.Command: 1185 start = self._prev 1186 temporary = self._match(TokenType.TEMPORARY) 1187 materialized = self._match_text_seq("MATERIALIZED") 1188 1189 kind = self._match_set(self.CREATABLES) and self._prev.text 1190 if not kind: 1191 return self._parse_as_command(start) 1192 1193 return self.expression( 1194 exp.Drop, 1195 comments=start.comments, 1196 exists=exists or self._parse_exists(), 1197 this=self._parse_table(schema=True), 1198 kind=kind, 1199 temporary=temporary, 1200 materialized=materialized, 1201 cascade=self._match_text_seq("CASCADE"), 1202 constraints=self._match_text_seq("CONSTRAINTS"), 1203 purge=self._match_text_seq("PURGE"), 1204 ) 1205 1206 def _parse_exists(self, not_: bool = False) -> t.Optional[bool]: 1207 return ( 1208 self._match_text_seq("IF") 1209 and (not not_ or self._match(TokenType.NOT)) 1210 and self._match(TokenType.EXISTS) 1211 ) 1212 1213 def _parse_create(self) -> exp.Create | exp.Command: 1214 # Note: this can't be None because we've matched a statement parser 1215 start = self._prev 1216 comments = self._prev_comments 1217 1218 replace = start.text.upper() == "REPLACE" or self._match_pair( 1219 TokenType.OR, TokenType.REPLACE 1220 ) 1221 unique = self._match(TokenType.UNIQUE) 1222 1223 if self._match_pair(TokenType.TABLE, TokenType.FUNCTION, advance=False): 1224 self._advance() 1225 1226 properties = None 1227 create_token = self._match_set(self.CREATABLES) and self._prev 1228 1229 if not create_token: 1230 # exp.Properties.Location.POST_CREATE 1231 properties = self._parse_properties() 1232 create_token = self._match_set(self.CREATABLES) and self._prev 1233 1234 if not properties or not create_token: 1235 return self._parse_as_command(start) 1236 1237 exists = self._parse_exists(not_=True) 1238 this = None 1239 expression: t.Optional[exp.Expression] = None 1240 indexes = None 1241 no_schema_binding = None 1242 begin = None 1243 clone = None 1244 1245 def extend_props(temp_props: t.Optional[exp.Properties]) -> None: 1246 nonlocal properties 1247 if properties and temp_props: 1248 properties.expressions.extend(temp_props.expressions) 1249 elif temp_props: 1250 properties = temp_props 1251 1252 if create_token.token_type in (TokenType.FUNCTION, TokenType.PROCEDURE): 1253 this = self._parse_user_defined_function(kind=create_token.token_type) 1254 1255 # exp.Properties.Location.POST_SCHEMA ("schema" here is the UDF's type signature) 1256 extend_props(self._parse_properties()) 1257 1258 self._match(TokenType.ALIAS) 1259 1260 if self._match(TokenType.COMMAND): 1261 expression = self._parse_as_command(self._prev) 1262 else: 1263 begin = self._match(TokenType.BEGIN) 1264 return_ = self._match_text_seq("RETURN") 1265 expression = self._parse_statement() 1266 1267 if return_: 1268 expression = self.expression(exp.Return, this=expression) 1269 elif create_token.token_type == TokenType.INDEX: 1270 this = self._parse_index(index=self._parse_id_var()) 1271 elif create_token.token_type in self.DB_CREATABLES: 1272 table_parts = self._parse_table_parts(schema=True) 1273 1274 # exp.Properties.Location.POST_NAME 1275 self._match(TokenType.COMMA) 1276 extend_props(self._parse_properties(before=True)) 1277 1278 this = self._parse_schema(this=table_parts) 1279 1280 # exp.Properties.Location.POST_SCHEMA and POST_WITH 1281 extend_props(self._parse_properties()) 1282 1283 self._match(TokenType.ALIAS) 1284 if not self._match_set(self.DDL_SELECT_TOKENS, advance=False): 1285 # exp.Properties.Location.POST_ALIAS 1286 extend_props(self._parse_properties()) 1287 1288 expression = self._parse_ddl_select() 1289 1290 if create_token.token_type == TokenType.TABLE: 1291 # exp.Properties.Location.POST_EXPRESSION 1292 extend_props(self._parse_properties()) 1293 1294 indexes = [] 1295 while True: 1296 index = self._parse_index() 1297 1298 # exp.Properties.Location.POST_INDEX 1299 extend_props(self._parse_properties()) 1300 1301 if not index: 1302 break 1303 else: 1304 self._match(TokenType.COMMA) 1305 indexes.append(index) 1306 elif create_token.token_type == TokenType.VIEW: 1307 if self._match_text_seq("WITH", "NO", "SCHEMA", "BINDING"): 1308 no_schema_binding = True 1309 1310 shallow = self._match_text_seq("SHALLOW") 1311 1312 if self._match_text_seq("CLONE"): 1313 clone = self._parse_table(schema=True) 1314 when = self._match_texts({"AT", "BEFORE"}) and self._prev.text.upper() 1315 clone_kind = ( 1316 self._match(TokenType.L_PAREN) 1317 and self._match_texts(self.CLONE_KINDS) 1318 and self._prev.text.upper() 1319 ) 1320 clone_expression = self._match(TokenType.FARROW) and self._parse_bitwise() 1321 self._match(TokenType.R_PAREN) 1322 clone = self.expression( 1323 exp.Clone, 1324 this=clone, 1325 when=when, 1326 kind=clone_kind, 1327 shallow=shallow, 1328 expression=clone_expression, 1329 ) 1330 1331 return self.expression( 1332 exp.Create, 1333 comments=comments, 1334 this=this, 1335 kind=create_token.text, 1336 replace=replace, 1337 unique=unique, 1338 expression=expression, 1339 exists=exists, 1340 properties=properties, 1341 indexes=indexes, 1342 no_schema_binding=no_schema_binding, 1343 begin=begin, 1344 clone=clone, 1345 ) 1346 1347 def _parse_property_before(self) -> t.Optional[exp.Expression]: 1348 # only used for teradata currently 1349 self._match(TokenType.COMMA) 1350 1351 kwargs = { 1352 "no": self._match_text_seq("NO"), 1353 "dual": self._match_text_seq("DUAL"), 1354 "before": self._match_text_seq("BEFORE"), 1355 "default": self._match_text_seq("DEFAULT"), 1356 "local": (self._match_text_seq("LOCAL") and "LOCAL") 1357 or (self._match_text_seq("NOT", "LOCAL") and "NOT LOCAL"), 1358 "after": self._match_text_seq("AFTER"), 1359 "minimum": self._match_texts(("MIN", "MINIMUM")), 1360 "maximum": self._match_texts(("MAX", "MAXIMUM")), 1361 } 1362 1363 if self._match_texts(self.PROPERTY_PARSERS): 1364 parser = self.PROPERTY_PARSERS[self._prev.text.upper()] 1365 try: 1366 return parser(self, **{k: v for k, v in kwargs.items() if v}) 1367 except TypeError: 1368 self.raise_error(f"Cannot parse property '{self._prev.text}'") 1369 1370 return None 1371 1372 def _parse_property(self) -> t.Optional[exp.Expression]: 1373 if self._match_texts(self.PROPERTY_PARSERS): 1374 return self.PROPERTY_PARSERS[self._prev.text.upper()](self) 1375 1376 if self._match_pair(TokenType.DEFAULT, TokenType.CHARACTER_SET): 1377 return self._parse_character_set(default=True) 1378 1379 if self._match_text_seq("COMPOUND", "SORTKEY"): 1380 return self._parse_sortkey(compound=True) 1381 1382 if self._match_text_seq("SQL", "SECURITY"): 1383 return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER")) 1384 1385 assignment = self._match_pair( 1386 TokenType.VAR, TokenType.EQ, advance=False 1387 ) or self._match_pair(TokenType.STRING, TokenType.EQ, advance=False) 1388 1389 if assignment: 1390 key = self._parse_var_or_string() 1391 self._match(TokenType.EQ) 1392 return self.expression( 1393 exp.Property, 1394 this=key, 1395 value=self._parse_column() or self._parse_var(any_token=True), 1396 ) 1397 1398 return None 1399 1400 def _parse_stored(self) -> exp.FileFormatProperty: 1401 self._match(TokenType.ALIAS) 1402 1403 input_format = self._parse_string() if self._match_text_seq("INPUTFORMAT") else None 1404 output_format = self._parse_string() if self._match_text_seq("OUTPUTFORMAT") else None 1405 1406 return self.expression( 1407 exp.FileFormatProperty, 1408 this=self.expression( 1409 exp.InputOutputFormat, input_format=input_format, output_format=output_format 1410 ) 1411 if input_format or output_format 1412 else self._parse_var_or_string() or self._parse_number() or self._parse_id_var(), 1413 ) 1414 1415 def _parse_property_assignment(self, exp_class: t.Type[E]) -> E: 1416 self._match(TokenType.EQ) 1417 self._match(TokenType.ALIAS) 1418 return self.expression(exp_class, this=self._parse_field()) 1419 1420 def _parse_properties(self, before: t.Optional[bool] = None) -> t.Optional[exp.Properties]: 1421 properties = [] 1422 while True: 1423 if before: 1424 prop = self._parse_property_before() 1425 else: 1426 prop = self._parse_property() 1427 1428 if not prop: 1429 break 1430 for p in ensure_list(prop): 1431 properties.append(p) 1432 1433 if properties: 1434 return self.expression(exp.Properties, expressions=properties) 1435 1436 return None 1437 1438 def _parse_fallback(self, no: bool = False) -> exp.FallbackProperty: 1439 return self.expression( 1440 exp.FallbackProperty, no=no, protection=self._match_text_seq("PROTECTION") 1441 ) 1442 1443 def _parse_volatile_property(self) -> exp.VolatileProperty | exp.StabilityProperty: 1444 if self._index >= 2: 1445 pre_volatile_token = self._tokens[self._index - 2] 1446 else: 1447 pre_volatile_token = None 1448 1449 if pre_volatile_token and pre_volatile_token.token_type in self.PRE_VOLATILE_TOKENS: 1450 return exp.VolatileProperty() 1451 1452 return self.expression(exp.StabilityProperty, this=exp.Literal.string("VOLATILE")) 1453 1454 def _parse_with_property( 1455 self, 1456 ) -> t.Optional[exp.Expression] | t.List[exp.Expression]: 1457 if self._match(TokenType.L_PAREN, advance=False): 1458 return self._parse_wrapped_csv(self._parse_property) 1459 1460 if self._match_text_seq("JOURNAL"): 1461 return self._parse_withjournaltable() 1462 1463 if self._match_text_seq("DATA"): 1464 return self._parse_withdata(no=False) 1465 elif self._match_text_seq("NO", "DATA"): 1466 return self._parse_withdata(no=True) 1467 1468 if not self._next: 1469 return None 1470 1471 return self._parse_withisolatedloading() 1472 1473 # https://dev.mysql.com/doc/refman/8.0/en/create-view.html 1474 def _parse_definer(self) -> t.Optional[exp.DefinerProperty]: 1475 self._match(TokenType.EQ) 1476 1477 user = self._parse_id_var() 1478 self._match(TokenType.PARAMETER) 1479 host = self._parse_id_var() or (self._match(TokenType.MOD) and self._prev.text) 1480 1481 if not user or not host: 1482 return None 1483 1484 return exp.DefinerProperty(this=f"{user}@{host}") 1485 1486 def _parse_withjournaltable(self) -> exp.WithJournalTableProperty: 1487 self._match(TokenType.TABLE) 1488 self._match(TokenType.EQ) 1489 return self.expression(exp.WithJournalTableProperty, this=self._parse_table_parts()) 1490 1491 def _parse_log(self, no: bool = False) -> exp.LogProperty: 1492 return self.expression(exp.LogProperty, no=no) 1493 1494 def _parse_journal(self, **kwargs) -> exp.JournalProperty: 1495 return self.expression(exp.JournalProperty, **kwargs) 1496 1497 def _parse_checksum(self) -> exp.ChecksumProperty: 1498 self._match(TokenType.EQ) 1499 1500 on = None 1501 if self._match(TokenType.ON): 1502 on = True 1503 elif self._match_text_seq("OFF"): 1504 on = False 1505 1506 return self.expression(exp.ChecksumProperty, on=on, default=self._match(TokenType.DEFAULT)) 1507 1508 def _parse_cluster(self) -> exp.Cluster: 1509 return self.expression(exp.Cluster, expressions=self._parse_csv(self._parse_ordered)) 1510 1511 def _parse_clustered_by(self) -> exp.ClusteredByProperty: 1512 self._match_text_seq("BY") 1513 1514 self._match_l_paren() 1515 expressions = self._parse_csv(self._parse_column) 1516 self._match_r_paren() 1517 1518 if self._match_text_seq("SORTED", "BY"): 1519 self._match_l_paren() 1520 sorted_by = self._parse_csv(self._parse_ordered) 1521 self._match_r_paren() 1522 else: 1523 sorted_by = None 1524 1525 self._match(TokenType.INTO) 1526 buckets = self._parse_number() 1527 self._match_text_seq("BUCKETS") 1528 1529 return self.expression( 1530 exp.ClusteredByProperty, 1531 expressions=expressions, 1532 sorted_by=sorted_by, 1533 buckets=buckets, 1534 ) 1535 1536 def _parse_copy_property(self) -> t.Optional[exp.CopyGrantsProperty]: 1537 if not self._match_text_seq("GRANTS"): 1538 self._retreat(self._index - 1) 1539 return None 1540 1541 return self.expression(exp.CopyGrantsProperty) 1542 1543 def _parse_freespace(self) -> exp.FreespaceProperty: 1544 self._match(TokenType.EQ) 1545 return self.expression( 1546 exp.FreespaceProperty, this=self._parse_number(), percent=self._match(TokenType.PERCENT) 1547 ) 1548 1549 def _parse_mergeblockratio( 1550 self, no: bool = False, default: bool = False 1551 ) -> exp.MergeBlockRatioProperty: 1552 if self._match(TokenType.EQ): 1553 return self.expression( 1554 exp.MergeBlockRatioProperty, 1555 this=self._parse_number(), 1556 percent=self._match(TokenType.PERCENT), 1557 ) 1558 1559 return self.expression(exp.MergeBlockRatioProperty, no=no, default=default) 1560 1561 def _parse_datablocksize( 1562 self, 1563 default: t.Optional[bool] = None, 1564 minimum: t.Optional[bool] = None, 1565 maximum: t.Optional[bool] = None, 1566 ) -> exp.DataBlocksizeProperty: 1567 self._match(TokenType.EQ) 1568 size = self._parse_number() 1569 1570 units = None 1571 if self._match_texts(("BYTES", "KBYTES", "KILOBYTES")): 1572 units = self._prev.text 1573 1574 return self.expression( 1575 exp.DataBlocksizeProperty, 1576 size=size, 1577 units=units, 1578 default=default, 1579 minimum=minimum, 1580 maximum=maximum, 1581 ) 1582 1583 def _parse_blockcompression(self) -> exp.BlockCompressionProperty: 1584 self._match(TokenType.EQ) 1585 always = self._match_text_seq("ALWAYS") 1586 manual = self._match_text_seq("MANUAL") 1587 never = self._match_text_seq("NEVER") 1588 default = self._match_text_seq("DEFAULT") 1589 1590 autotemp = None 1591 if self._match_text_seq("AUTOTEMP"): 1592 autotemp = self._parse_schema() 1593 1594 return self.expression( 1595 exp.BlockCompressionProperty, 1596 always=always, 1597 manual=manual, 1598 never=never, 1599 default=default, 1600 autotemp=autotemp, 1601 ) 1602 1603 def _parse_withisolatedloading(self) -> exp.IsolatedLoadingProperty: 1604 no = self._match_text_seq("NO") 1605 concurrent = self._match_text_seq("CONCURRENT") 1606 self._match_text_seq("ISOLATED", "LOADING") 1607 for_all = self._match_text_seq("FOR", "ALL") 1608 for_insert = self._match_text_seq("FOR", "INSERT") 1609 for_none = self._match_text_seq("FOR", "NONE") 1610 return self.expression( 1611 exp.IsolatedLoadingProperty, 1612 no=no, 1613 concurrent=concurrent, 1614 for_all=for_all, 1615 for_insert=for_insert, 1616 for_none=for_none, 1617 ) 1618 1619 def _parse_locking(self) -> exp.LockingProperty: 1620 if self._match(TokenType.TABLE): 1621 kind = "TABLE" 1622 elif self._match(TokenType.VIEW): 1623 kind = "VIEW" 1624 elif self._match(TokenType.ROW): 1625 kind = "ROW" 1626 elif self._match_text_seq("DATABASE"): 1627 kind = "DATABASE" 1628 else: 1629 kind = None 1630 1631 if kind in ("DATABASE", "TABLE", "VIEW"): 1632 this = self._parse_table_parts() 1633 else: 1634 this = None 1635 1636 if self._match(TokenType.FOR): 1637 for_or_in = "FOR" 1638 elif self._match(TokenType.IN): 1639 for_or_in = "IN" 1640 else: 1641 for_or_in = None 1642 1643 if self._match_text_seq("ACCESS"): 1644 lock_type = "ACCESS" 1645 elif self._match_texts(("EXCL", "EXCLUSIVE")): 1646 lock_type = "EXCLUSIVE" 1647 elif self._match_text_seq("SHARE"): 1648 lock_type = "SHARE" 1649 elif self._match_text_seq("READ"): 1650 lock_type = "READ" 1651 elif self._match_text_seq("WRITE"): 1652 lock_type = "WRITE" 1653 elif self._match_text_seq("CHECKSUM"): 1654 lock_type = "CHECKSUM" 1655 else: 1656 lock_type = None 1657 1658 override = self._match_text_seq("OVERRIDE") 1659 1660 return self.expression( 1661 exp.LockingProperty, 1662 this=this, 1663 kind=kind, 1664 for_or_in=for_or_in, 1665 lock_type=lock_type, 1666 override=override, 1667 ) 1668 1669 def _parse_partition_by(self) -> t.List[exp.Expression]: 1670 if self._match(TokenType.PARTITION_BY): 1671 return self._parse_csv(self._parse_conjunction) 1672 return [] 1673 1674 def _parse_partitioned_by(self) -> exp.PartitionedByProperty: 1675 self._match(TokenType.EQ) 1676 return self.expression( 1677 exp.PartitionedByProperty, 1678 this=self._parse_schema() or self._parse_bracket(self._parse_field()), 1679 ) 1680 1681 def _parse_withdata(self, no: bool = False) -> exp.WithDataProperty: 1682 if self._match_text_seq("AND", "STATISTICS"): 1683 statistics = True 1684 elif self._match_text_seq("AND", "NO", "STATISTICS"): 1685 statistics = False 1686 else: 1687 statistics = None 1688 1689 return self.expression(exp.WithDataProperty, no=no, statistics=statistics) 1690 1691 def _parse_no_property(self) -> t.Optional[exp.NoPrimaryIndexProperty]: 1692 if self._match_text_seq("PRIMARY", "INDEX"): 1693 return exp.NoPrimaryIndexProperty() 1694 return None 1695 1696 def _parse_on_property(self) -> t.Optional[exp.Expression]: 1697 if self._match_text_seq("COMMIT", "PRESERVE", "ROWS"): 1698 return exp.OnCommitProperty() 1699 if self._match_text_seq("COMMIT", "DELETE", "ROWS"): 1700 return exp.OnCommitProperty(delete=True) 1701 return self.expression(exp.OnProperty, this=self._parse_schema(self._parse_id_var())) 1702 1703 def _parse_distkey(self) -> exp.DistKeyProperty: 1704 return self.expression(exp.DistKeyProperty, this=self._parse_wrapped(self._parse_id_var)) 1705 1706 def _parse_create_like(self) -> t.Optional[exp.LikeProperty]: 1707 table = self._parse_table(schema=True) 1708 1709 options = [] 1710 while self._match_texts(("INCLUDING", "EXCLUDING")): 1711 this = self._prev.text.upper() 1712 1713 id_var = self._parse_id_var() 1714 if not id_var: 1715 return None 1716 1717 options.append( 1718 self.expression(exp.Property, this=this, value=exp.var(id_var.this.upper())) 1719 ) 1720 1721 return self.expression(exp.LikeProperty, this=table, expressions=options) 1722 1723 def _parse_sortkey(self, compound: bool = False) -> exp.SortKeyProperty: 1724 return self.expression( 1725 exp.SortKeyProperty, this=self._parse_wrapped_id_vars(), compound=compound 1726 ) 1727 1728 def _parse_character_set(self, default: bool = False) -> exp.CharacterSetProperty: 1729 self._match(TokenType.EQ) 1730 return self.expression( 1731 exp.CharacterSetProperty, this=self._parse_var_or_string(), default=default 1732 ) 1733 1734 def _parse_returns(self) -> exp.ReturnsProperty: 1735 value: t.Optional[exp.Expression] 1736 is_table = self._match(TokenType.TABLE) 1737 1738 if is_table: 1739 if self._match(TokenType.LT): 1740 value = self.expression( 1741 exp.Schema, 1742 this="TABLE", 1743 expressions=self._parse_csv(self._parse_struct_types), 1744 ) 1745 if not self._match(TokenType.GT): 1746 self.raise_error("Expecting >") 1747 else: 1748 value = self._parse_schema(exp.var("TABLE")) 1749 else: 1750 value = self._parse_types() 1751 1752 return self.expression(exp.ReturnsProperty, this=value, is_table=is_table) 1753 1754 def _parse_describe(self) -> exp.Describe: 1755 kind = self._match_set(self.CREATABLES) and self._prev.text 1756 this = self._parse_table() 1757 return self.expression(exp.Describe, this=this, kind=kind) 1758 1759 def _parse_insert(self) -> exp.Insert: 1760 comments = ensure_list(self._prev_comments) 1761 overwrite = self._match(TokenType.OVERWRITE) 1762 ignore = self._match(TokenType.IGNORE) 1763 local = self._match_text_seq("LOCAL") 1764 alternative = None 1765 1766 if self._match_text_seq("DIRECTORY"): 1767 this: t.Optional[exp.Expression] = self.expression( 1768 exp.Directory, 1769 this=self._parse_var_or_string(), 1770 local=local, 1771 row_format=self._parse_row_format(match_row=True), 1772 ) 1773 else: 1774 if self._match(TokenType.OR): 1775 alternative = self._match_texts(self.INSERT_ALTERNATIVES) and self._prev.text 1776 1777 self._match(TokenType.INTO) 1778 comments += ensure_list(self._prev_comments) 1779 self._match(TokenType.TABLE) 1780 this = self._parse_table(schema=True) 1781 1782 returning = self._parse_returning() 1783 1784 return self.expression( 1785 exp.Insert, 1786 comments=comments, 1787 this=this, 1788 by_name=self._match_text_seq("BY", "NAME"), 1789 exists=self._parse_exists(), 1790 partition=self._parse_partition(), 1791 where=self._match_pair(TokenType.REPLACE, TokenType.WHERE) 1792 and self._parse_conjunction(), 1793 expression=self._parse_ddl_select(), 1794 conflict=self._parse_on_conflict(), 1795 returning=returning or self._parse_returning(), 1796 overwrite=overwrite, 1797 alternative=alternative, 1798 ignore=ignore, 1799 ) 1800 1801 def _parse_on_conflict(self) -> t.Optional[exp.OnConflict]: 1802 conflict = self._match_text_seq("ON", "CONFLICT") 1803 duplicate = self._match_text_seq("ON", "DUPLICATE", "KEY") 1804 1805 if not conflict and not duplicate: 1806 return None 1807 1808 nothing = None 1809 expressions = None 1810 key = None 1811 constraint = None 1812 1813 if conflict: 1814 if self._match_text_seq("ON", "CONSTRAINT"): 1815 constraint = self._parse_id_var() 1816 else: 1817 key = self._parse_csv(self._parse_value) 1818 1819 self._match_text_seq("DO") 1820 if self._match_text_seq("NOTHING"): 1821 nothing = True 1822 else: 1823 self._match(TokenType.UPDATE) 1824 self._match(TokenType.SET) 1825 expressions = self._parse_csv(self._parse_equality) 1826 1827 return self.expression( 1828 exp.OnConflict, 1829 duplicate=duplicate, 1830 expressions=expressions, 1831 nothing=nothing, 1832 key=key, 1833 constraint=constraint, 1834 ) 1835 1836 def _parse_returning(self) -> t.Optional[exp.Returning]: 1837 if not self._match(TokenType.RETURNING): 1838 return None 1839 return self.expression( 1840 exp.Returning, 1841 expressions=self._parse_csv(self._parse_expression), 1842 into=self._match(TokenType.INTO) and self._parse_table_part(), 1843 ) 1844 1845 def _parse_row(self) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]: 1846 if not self._match(TokenType.FORMAT): 1847 return None 1848 return self._parse_row_format() 1849 1850 def _parse_row_format( 1851 self, match_row: bool = False 1852 ) -> t.Optional[exp.RowFormatSerdeProperty | exp.RowFormatDelimitedProperty]: 1853 if match_row and not self._match_pair(TokenType.ROW, TokenType.FORMAT): 1854 return None 1855 1856 if self._match_text_seq("SERDE"): 1857 this = self._parse_string() 1858 1859 serde_properties = None 1860 if self._match(TokenType.SERDE_PROPERTIES): 1861 serde_properties = self.expression( 1862 exp.SerdeProperties, expressions=self._parse_wrapped_csv(self._parse_property) 1863 ) 1864 1865 return self.expression( 1866 exp.RowFormatSerdeProperty, this=this, serde_properties=serde_properties 1867 ) 1868 1869 self._match_text_seq("DELIMITED") 1870 1871 kwargs = {} 1872 1873 if self._match_text_seq("FIELDS", "TERMINATED", "BY"): 1874 kwargs["fields"] = self._parse_string() 1875 if self._match_text_seq("ESCAPED", "BY"): 1876 kwargs["escaped"] = self._parse_string() 1877 if self._match_text_seq("COLLECTION", "ITEMS", "TERMINATED", "BY"): 1878 kwargs["collection_items"] = self._parse_string() 1879 if self._match_text_seq("MAP", "KEYS", "TERMINATED", "BY"): 1880 kwargs["map_keys"] = self._parse_string() 1881 if self._match_text_seq("LINES", "TERMINATED", "BY"): 1882 kwargs["lines"] = self._parse_string() 1883 if self._match_text_seq("NULL", "DEFINED", "AS"): 1884 kwargs["null"] = self._parse_string() 1885 1886 return self.expression(exp.RowFormatDelimitedProperty, **kwargs) # type: ignore 1887 1888 def _parse_load(self) -> exp.LoadData | exp.Command: 1889 if self._match_text_seq("DATA"): 1890 local = self._match_text_seq("LOCAL") 1891 self._match_text_seq("INPATH") 1892 inpath = self._parse_string() 1893 overwrite = self._match(TokenType.OVERWRITE) 1894 self._match_pair(TokenType.INTO, TokenType.TABLE) 1895 1896 return self.expression( 1897 exp.LoadData, 1898 this=self._parse_table(schema=True), 1899 local=local, 1900 overwrite=overwrite, 1901 inpath=inpath, 1902 partition=self._parse_partition(), 1903 input_format=self._match_text_seq("INPUTFORMAT") and self._parse_string(), 1904 serde=self._match_text_seq("SERDE") and self._parse_string(), 1905 ) 1906 return self._parse_as_command(self._prev) 1907 1908 def _parse_delete(self) -> exp.Delete: 1909 # This handles MySQL's "Multiple-Table Syntax" 1910 # https://dev.mysql.com/doc/refman/8.0/en/delete.html 1911 tables = None 1912 comments = self._prev_comments 1913 if not self._match(TokenType.FROM, advance=False): 1914 tables = self._parse_csv(self._parse_table) or None 1915 1916 returning = self._parse_returning() 1917 1918 return self.expression( 1919 exp.Delete, 1920 comments=comments, 1921 tables=tables, 1922 this=self._match(TokenType.FROM) and self._parse_table(joins=True), 1923 using=self._match(TokenType.USING) and self._parse_table(joins=True), 1924 where=self._parse_where(), 1925 returning=returning or self._parse_returning(), 1926 limit=self._parse_limit(), 1927 ) 1928 1929 def _parse_update(self) -> exp.Update: 1930 comments = self._prev_comments 1931 this = self._parse_table(alias_tokens=self.UPDATE_ALIAS_TOKENS) 1932 expressions = self._match(TokenType.SET) and self._parse_csv(self._parse_equality) 1933 returning = self._parse_returning() 1934 return self.expression( 1935 exp.Update, 1936 comments=comments, 1937 **{ # type: ignore 1938 "this": this, 1939 "expressions": expressions, 1940 "from": self._parse_from(joins=True), 1941 "where": self._parse_where(), 1942 "returning": returning or self._parse_returning(), 1943 "order": self._parse_order(), 1944 "limit": self._parse_limit(), 1945 }, 1946 ) 1947 1948 def _parse_uncache(self) -> exp.Uncache: 1949 if not self._match(TokenType.TABLE): 1950 self.raise_error("Expecting TABLE after UNCACHE") 1951 1952 return self.expression( 1953 exp.Uncache, exists=self._parse_exists(), this=self._parse_table(schema=True) 1954 ) 1955 1956 def _parse_cache(self) -> exp.Cache: 1957 lazy = self._match_text_seq("LAZY") 1958 self._match(TokenType.TABLE) 1959 table = self._parse_table(schema=True) 1960 1961 options = [] 1962 if self._match_text_seq("OPTIONS"): 1963 self._match_l_paren() 1964 k = self._parse_string() 1965 self._match(TokenType.EQ) 1966 v = self._parse_string() 1967 options = [k, v] 1968 self._match_r_paren() 1969 1970 self._match(TokenType.ALIAS) 1971 return self.expression( 1972 exp.Cache, 1973 this=table, 1974 lazy=lazy, 1975 options=options, 1976 expression=self._parse_select(nested=True), 1977 ) 1978 1979 def _parse_partition(self) -> t.Optional[exp.Partition]: 1980 if not self._match(TokenType.PARTITION): 1981 return None 1982 1983 return self.expression( 1984 exp.Partition, expressions=self._parse_wrapped_csv(self._parse_conjunction) 1985 ) 1986 1987 def _parse_value(self) -> exp.Tuple: 1988 if self._match(TokenType.L_PAREN): 1989 expressions = self._parse_csv(self._parse_conjunction) 1990 self._match_r_paren() 1991 return self.expression(exp.Tuple, expressions=expressions) 1992 1993 # In presto we can have VALUES 1, 2 which results in 1 column & 2 rows. 1994 # https://prestodb.io/docs/current/sql/values.html 1995 return self.expression(exp.Tuple, expressions=[self._parse_conjunction()]) 1996 1997 def _parse_projections(self) -> t.List[exp.Expression]: 1998 return self._parse_expressions() 1999 2000 def _parse_select( 2001 self, nested: bool = False, table: bool = False, parse_subquery_alias: bool = True 2002 ) -> t.Optional[exp.Expression]: 2003 cte = self._parse_with() 2004 2005 if cte: 2006 this = self._parse_statement() 2007 2008 if not this: 2009 self.raise_error("Failed to parse any statement following CTE") 2010 return cte 2011 2012 if "with" in this.arg_types: 2013 this.set("with", cte) 2014 else: 2015 self.raise_error(f"{this.key} does not support CTE") 2016 this = cte 2017 2018 return this 2019 2020 # duckdb supports leading with FROM x 2021 from_ = self._parse_from() if self._match(TokenType.FROM, advance=False) else None 2022 2023 if self._match(TokenType.SELECT): 2024 comments = self._prev_comments 2025 2026 hint = self._parse_hint() 2027 all_ = self._match(TokenType.ALL) 2028 distinct = self._match_set(self.DISTINCT_TOKENS) 2029 2030 kind = ( 2031 self._match(TokenType.ALIAS) 2032 and self._match_texts(("STRUCT", "VALUE")) 2033 and self._prev.text 2034 ) 2035 2036 if distinct: 2037 distinct = self.expression( 2038 exp.Distinct, 2039 on=self._parse_value() if self._match(TokenType.ON) else None, 2040 ) 2041 2042 if all_ and distinct: 2043 self.raise_error("Cannot specify both ALL and DISTINCT after SELECT") 2044 2045 limit = self._parse_limit(top=True) 2046 projections = self._parse_projections() 2047 2048 this = self.expression( 2049 exp.Select, 2050 kind=kind, 2051 hint=hint, 2052 distinct=distinct, 2053 expressions=projections, 2054 limit=limit, 2055 ) 2056 this.comments = comments 2057 2058 into = self._parse_into() 2059 if into: 2060 this.set("into", into) 2061 2062 if not from_: 2063 from_ = self._parse_from() 2064 2065 if from_: 2066 this.set("from", from_) 2067 2068 this = self._parse_query_modifiers(this) 2069 elif (table or nested) and self._match(TokenType.L_PAREN): 2070 if self._match(TokenType.PIVOT): 2071 this = self._parse_simplified_pivot() 2072 elif self._match(TokenType.FROM): 2073 this = exp.select("*").from_( 2074 t.cast(exp.From, self._parse_from(skip_from_token=True)) 2075 ) 2076 else: 2077 this = self._parse_table() if table else self._parse_select(nested=True) 2078 this = self._parse_set_operations(self._parse_query_modifiers(this)) 2079 2080 self._match_r_paren() 2081 2082 # We return early here so that the UNION isn't attached to the subquery by the 2083 # following call to _parse_set_operations, but instead becomes the parent node 2084 return self._parse_subquery(this, parse_alias=parse_subquery_alias) 2085 elif self._match(TokenType.VALUES): 2086 this = self.expression( 2087 exp.Values, 2088 expressions=self._parse_csv(self._parse_value), 2089 alias=self._parse_table_alias(), 2090 ) 2091 elif from_: 2092 this = exp.select("*").from_(from_.this, copy=False) 2093 else: 2094 this = None 2095 2096 return self._parse_set_operations(this) 2097 2098 def _parse_with(self, skip_with_token: bool = False) -> t.Optional[exp.With]: 2099 if not skip_with_token and not self._match(TokenType.WITH): 2100 return None 2101 2102 comments = self._prev_comments 2103 recursive = self._match(TokenType.RECURSIVE) 2104 2105 expressions = [] 2106 while True: 2107 expressions.append(self._parse_cte()) 2108 2109 if not self._match(TokenType.COMMA) and not self._match(TokenType.WITH): 2110 break 2111 else: 2112 self._match(TokenType.WITH) 2113 2114 return self.expression( 2115 exp.With, comments=comments, expressions=expressions, recursive=recursive 2116 ) 2117 2118 def _parse_cte(self) -> exp.CTE: 2119 alias = self._parse_table_alias() 2120 if not alias or not alias.this: 2121 self.raise_error("Expected CTE to have alias") 2122 2123 self._match(TokenType.ALIAS) 2124 return self.expression( 2125 exp.CTE, this=self._parse_wrapped(self._parse_statement), alias=alias 2126 ) 2127 2128 def _parse_table_alias( 2129 self, alias_tokens: t.Optional[t.Collection[TokenType]] = None 2130 ) -> t.Optional[exp.TableAlias]: 2131 any_token = self._match(TokenType.ALIAS) 2132 alias = ( 2133 self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS) 2134 or self._parse_string_as_identifier() 2135 ) 2136 2137 index = self._index 2138 if self._match(TokenType.L_PAREN): 2139 columns = self._parse_csv(self._parse_function_parameter) 2140 self._match_r_paren() if columns else self._retreat(index) 2141 else: 2142 columns = None 2143 2144 if not alias and not columns: 2145 return None 2146 2147 return self.expression(exp.TableAlias, this=alias, columns=columns) 2148 2149 def _parse_subquery( 2150 self, this: t.Optional[exp.Expression], parse_alias: bool = True 2151 ) -> t.Optional[exp.Subquery]: 2152 if not this: 2153 return None 2154 2155 return self.expression( 2156 exp.Subquery, 2157 this=this, 2158 pivots=self._parse_pivots(), 2159 alias=self._parse_table_alias() if parse_alias else None, 2160 ) 2161 2162 def _parse_query_modifiers( 2163 self, this: t.Optional[exp.Expression] 2164 ) -> t.Optional[exp.Expression]: 2165 if isinstance(this, self.MODIFIABLES): 2166 for join in iter(self._parse_join, None): 2167 this.append("joins", join) 2168 for lateral in iter(self._parse_lateral, None): 2169 this.append("laterals", lateral) 2170 2171 while True: 2172 if self._match_set(self.QUERY_MODIFIER_PARSERS, advance=False): 2173 parser = self.QUERY_MODIFIER_PARSERS[self._curr.token_type] 2174 key, expression = parser(self) 2175 2176 if expression: 2177 this.set(key, expression) 2178 if key == "limit": 2179 offset = expression.args.pop("offset", None) 2180 if offset: 2181 this.set("offset", exp.Offset(expression=offset)) 2182 continue 2183 break 2184 return this 2185 2186 def _parse_hint(self) -> t.Optional[exp.Hint]: 2187 if self._match(TokenType.HINT): 2188 hints = [] 2189 for hint in iter(lambda: self._parse_csv(self._parse_function), []): 2190 hints.extend(hint) 2191 2192 if not self._match_pair(TokenType.STAR, TokenType.SLASH): 2193 self.raise_error("Expected */ after HINT") 2194 2195 return self.expression(exp.Hint, expressions=hints) 2196 2197 return None 2198 2199 def _parse_into(self) -> t.Optional[exp.Into]: 2200 if not self._match(TokenType.INTO): 2201 return None 2202 2203 temp = self._match(TokenType.TEMPORARY) 2204 unlogged = self._match_text_seq("UNLOGGED") 2205 self._match(TokenType.TABLE) 2206 2207 return self.expression( 2208 exp.Into, this=self._parse_table(schema=True), temporary=temp, unlogged=unlogged 2209 ) 2210 2211 def _parse_from( 2212 self, joins: bool = False, skip_from_token: bool = False 2213 ) -> t.Optional[exp.From]: 2214 if not skip_from_token and not self._match(TokenType.FROM): 2215 return None 2216 2217 return self.expression( 2218 exp.From, comments=self._prev_comments, this=self._parse_table(joins=joins) 2219 ) 2220 2221 def _parse_match_recognize(self) -> t.Optional[exp.MatchRecognize]: 2222 if not self._match(TokenType.MATCH_RECOGNIZE): 2223 return None 2224 2225 self._match_l_paren() 2226 2227 partition = self._parse_partition_by() 2228 order = self._parse_order() 2229 measures = self._parse_expressions() if self._match_text_seq("MEASURES") else None 2230 2231 if self._match_text_seq("ONE", "ROW", "PER", "MATCH"): 2232 rows = exp.var("ONE ROW PER MATCH") 2233 elif self._match_text_seq("ALL", "ROWS", "PER", "MATCH"): 2234 text = "ALL ROWS PER MATCH" 2235 if self._match_text_seq("SHOW", "EMPTY", "MATCHES"): 2236 text += f" SHOW EMPTY MATCHES" 2237 elif self._match_text_seq("OMIT", "EMPTY", "MATCHES"): 2238 text += f" OMIT EMPTY MATCHES" 2239 elif self._match_text_seq("WITH", "UNMATCHED", "ROWS"): 2240 text += f" WITH UNMATCHED ROWS" 2241 rows = exp.var(text) 2242 else: 2243 rows = None 2244 2245 if self._match_text_seq("AFTER", "MATCH", "SKIP"): 2246 text = "AFTER MATCH SKIP" 2247 if self._match_text_seq("PAST", "LAST", "ROW"): 2248 text += f" PAST LAST ROW" 2249 elif self._match_text_seq("TO", "NEXT", "ROW"): 2250 text += f" TO NEXT ROW" 2251 elif self._match_text_seq("TO", "FIRST"): 2252 text += f" TO FIRST {self._advance_any().text}" # type: ignore 2253 elif self._match_text_seq("TO", "LAST"): 2254 text += f" TO LAST {self._advance_any().text}" # type: ignore 2255 after = exp.var(text) 2256 else: 2257 after = None 2258 2259 if self._match_text_seq("PATTERN"): 2260 self._match_l_paren() 2261 2262 if not self._curr: 2263 self.raise_error("Expecting )", self._curr) 2264 2265 paren = 1 2266 start = self._curr 2267 2268 while self._curr and paren > 0: 2269 if self._curr.token_type == TokenType.L_PAREN: 2270 paren += 1 2271 if self._curr.token_type == TokenType.R_PAREN: 2272 paren -= 1 2273 2274 end = self._prev 2275 self._advance() 2276 2277 if paren > 0: 2278 self.raise_error("Expecting )", self._curr) 2279 2280 pattern = exp.var(self._find_sql(start, end)) 2281 else: 2282 pattern = None 2283 2284 define = ( 2285 self._parse_csv( 2286 lambda: self.expression( 2287 exp.Alias, 2288 alias=self._parse_id_var(any_token=True), 2289 this=self._match(TokenType.ALIAS) and self._parse_conjunction(), 2290 ) 2291 ) 2292 if self._match_text_seq("DEFINE") 2293 else None 2294 ) 2295 2296 self._match_r_paren() 2297 2298 return self.expression( 2299 exp.MatchRecognize, 2300 partition_by=partition, 2301 order=order, 2302 measures=measures, 2303 rows=rows, 2304 after=after, 2305 pattern=pattern, 2306 define=define, 2307 alias=self._parse_table_alias(), 2308 ) 2309 2310 def _parse_lateral(self) -> t.Optional[exp.Lateral]: 2311 outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY) 2312 cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY) 2313 2314 if outer_apply or cross_apply: 2315 this = self._parse_select(table=True) 2316 view = None 2317 outer = not cross_apply 2318 elif self._match(TokenType.LATERAL): 2319 this = self._parse_select(table=True) 2320 view = self._match(TokenType.VIEW) 2321 outer = self._match(TokenType.OUTER) 2322 else: 2323 return None 2324 2325 if not this: 2326 this = ( 2327 self._parse_unnest() 2328 or self._parse_function() 2329 or self._parse_id_var(any_token=False) 2330 ) 2331 2332 while self._match(TokenType.DOT): 2333 this = exp.Dot( 2334 this=this, 2335 expression=self._parse_function() or self._parse_id_var(any_token=False), 2336 ) 2337 2338 if view: 2339 table = self._parse_id_var(any_token=False) 2340 columns = self._parse_csv(self._parse_id_var) if self._match(TokenType.ALIAS) else [] 2341 table_alias: t.Optional[exp.TableAlias] = self.expression( 2342 exp.TableAlias, this=table, columns=columns 2343 ) 2344 elif isinstance(this, exp.Subquery) and this.alias: 2345 # Ensures parity between the Subquery's and the Lateral's "alias" args 2346 table_alias = this.args["alias"].copy() 2347 else: 2348 table_alias = self._parse_table_alias() 2349 2350 return self.expression(exp.Lateral, this=this, view=view, outer=outer, alias=table_alias) 2351 2352 def _parse_join_parts( 2353 self, 2354 ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]: 2355 return ( 2356 self._match_set(self.JOIN_METHODS) and self._prev, 2357 self._match_set(self.JOIN_SIDES) and self._prev, 2358 self._match_set(self.JOIN_KINDS) and self._prev, 2359 ) 2360 2361 def _parse_join( 2362 self, skip_join_token: bool = False, parse_bracket: bool = False 2363 ) -> t.Optional[exp.Join]: 2364 if self._match(TokenType.COMMA): 2365 return self.expression(exp.Join, this=self._parse_table()) 2366 2367 index = self._index 2368 method, side, kind = self._parse_join_parts() 2369 hint = self._prev.text if self._match_texts(self.JOIN_HINTS) else None 2370 join = self._match(TokenType.JOIN) 2371 2372 if not skip_join_token and not join: 2373 self._retreat(index) 2374 kind = None 2375 method = None 2376 side = None 2377 2378 outer_apply = self._match_pair(TokenType.OUTER, TokenType.APPLY, False) 2379 cross_apply = self._match_pair(TokenType.CROSS, TokenType.APPLY, False) 2380 2381 if not skip_join_token and not join and not outer_apply and not cross_apply: 2382 return None 2383 2384 if outer_apply: 2385 side = Token(TokenType.LEFT, "LEFT") 2386 2387 kwargs: t.Dict[str, t.Any] = {"this": self._parse_table(parse_bracket=parse_bracket)} 2388 2389 if method: 2390 kwargs["method"] = method.text 2391 if side: 2392 kwargs["side"] = side.text 2393 if kind: 2394 kwargs["kind"] = kind.text 2395 if hint: 2396 kwargs["hint"] = hint 2397 2398 if self._match(TokenType.ON): 2399 kwargs["on"] = self._parse_conjunction() 2400 elif self._match(TokenType.USING): 2401 kwargs["using"] = self._parse_wrapped_id_vars() 2402 elif not (kind and kind.token_type == TokenType.CROSS): 2403 index = self._index 2404 joins = self._parse_joins() 2405 2406 if joins and self._match(TokenType.ON): 2407 kwargs["on"] = self._parse_conjunction() 2408 elif joins and self._match(TokenType.USING): 2409 kwargs["using"] = self._parse_wrapped_id_vars() 2410 else: 2411 joins = None 2412 self._retreat(index) 2413 2414 kwargs["this"].set("joins", joins) 2415 2416 comments = [c for token in (method, side, kind) if token for c in token.comments] 2417 return self.expression(exp.Join, comments=comments, **kwargs) 2418 2419 def _parse_index( 2420 self, 2421 index: t.Optional[exp.Expression] = None, 2422 ) -> t.Optional[exp.Index]: 2423 if index: 2424 unique = None 2425 primary = None 2426 amp = None 2427 2428 self._match(TokenType.ON) 2429 self._match(TokenType.TABLE) # hive 2430 table = self._parse_table_parts(schema=True) 2431 else: 2432 unique = self._match(TokenType.UNIQUE) 2433 primary = self._match_text_seq("PRIMARY") 2434 amp = self._match_text_seq("AMP") 2435 2436 if not self._match(TokenType.INDEX): 2437 return None 2438 2439 index = self._parse_id_var() 2440 table = None 2441 2442 using = self._parse_field() if self._match(TokenType.USING) else None 2443 2444 if self._match(TokenType.L_PAREN, advance=False): 2445 columns = self._parse_wrapped_csv(self._parse_ordered) 2446 else: 2447 columns = None 2448 2449 return self.expression( 2450 exp.Index, 2451 this=index, 2452 table=table, 2453 using=using, 2454 columns=columns, 2455 unique=unique, 2456 primary=primary, 2457 amp=amp, 2458 partition_by=self._parse_partition_by(), 2459 ) 2460 2461 def _parse_table_hints(self) -> t.Optional[t.List[exp.Expression]]: 2462 hints: t.List[exp.Expression] = [] 2463 if self._match_pair(TokenType.WITH, TokenType.L_PAREN): 2464 # https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver16 2465 hints.append( 2466 self.expression( 2467 exp.WithTableHint, 2468 expressions=self._parse_csv( 2469 lambda: self._parse_function() or self._parse_var(any_token=True) 2470 ), 2471 ) 2472 ) 2473 self._match_r_paren() 2474 else: 2475 # https://dev.mysql.com/doc/refman/8.0/en/index-hints.html 2476 while self._match_set(self.TABLE_INDEX_HINT_TOKENS): 2477 hint = exp.IndexTableHint(this=self._prev.text.upper()) 2478 2479 self._match_texts({"INDEX", "KEY"}) 2480 if self._match(TokenType.FOR): 2481 hint.set("target", self._advance_any() and self._prev.text.upper()) 2482 2483 hint.set("expressions", self._parse_wrapped_id_vars()) 2484 hints.append(hint) 2485 2486 return hints or None 2487 2488 def _parse_table_part(self, schema: bool = False) -> t.Optional[exp.Expression]: 2489 return ( 2490 (not schema and self._parse_function(optional_parens=False)) 2491 or self._parse_id_var(any_token=False) 2492 or self._parse_string_as_identifier() 2493 or self._parse_placeholder() 2494 ) 2495 2496 def _parse_table_parts(self, schema: bool = False) -> exp.Table: 2497 catalog = None 2498 db = None 2499 table = self._parse_table_part(schema=schema) 2500 2501 while self._match(TokenType.DOT): 2502 if catalog: 2503 # This allows nesting the table in arbitrarily many dot expressions if needed 2504 table = self.expression( 2505 exp.Dot, this=table, expression=self._parse_table_part(schema=schema) 2506 ) 2507 else: 2508 catalog = db 2509 db = table 2510 table = self._parse_table_part(schema=schema) 2511 2512 if not table: 2513 self.raise_error(f"Expected table name but got {self._curr}") 2514 2515 return self.expression( 2516 exp.Table, this=table, db=db, catalog=catalog, pivots=self._parse_pivots() 2517 ) 2518 2519 def _parse_table( 2520 self, 2521 schema: bool = False, 2522 joins: bool = False, 2523 alias_tokens: t.Optional[t.Collection[TokenType]] = None, 2524 parse_bracket: bool = False, 2525 ) -> t.Optional[exp.Expression]: 2526 lateral = self._parse_lateral() 2527 if lateral: 2528 return lateral 2529 2530 unnest = self._parse_unnest() 2531 if unnest: 2532 return unnest 2533 2534 values = self._parse_derived_table_values() 2535 if values: 2536 return values 2537 2538 subquery = self._parse_select(table=True) 2539 if subquery: 2540 if not subquery.args.get("pivots"): 2541 subquery.set("pivots", self._parse_pivots()) 2542 return subquery 2543 2544 bracket = parse_bracket and self._parse_bracket(None) 2545 bracket = self.expression(exp.Table, this=bracket) if bracket else None 2546 this: exp.Expression = bracket or self._parse_table_parts(schema=schema) 2547 2548 if schema: 2549 return self._parse_schema(this=this) 2550 2551 version = self._parse_version() 2552 2553 if version: 2554 this.set("version", version) 2555 2556 if self.ALIAS_POST_TABLESAMPLE: 2557 table_sample = self._parse_table_sample() 2558 2559 alias = self._parse_table_alias(alias_tokens=alias_tokens or self.TABLE_ALIAS_TOKENS) 2560 if alias: 2561 this.set("alias", alias) 2562 2563 this.set("hints", self._parse_table_hints()) 2564 2565 if not this.args.get("pivots"): 2566 this.set("pivots", self._parse_pivots()) 2567 2568 if not self.ALIAS_POST_TABLESAMPLE: 2569 table_sample = self._parse_table_sample() 2570 2571 if table_sample: 2572 table_sample.set("this", this) 2573 this = table_sample 2574 2575 if joins: 2576 for join in iter(self._parse_join, None): 2577 this.append("joins", join) 2578 2579 return this 2580 2581 def _parse_version(self) -> t.Optional[exp.Version]: 2582 if self._match(TokenType.TIMESTAMP_SNAPSHOT): 2583 this = "TIMESTAMP" 2584 elif self._match(TokenType.VERSION_SNAPSHOT): 2585 this = "VERSION" 2586 else: 2587 return None 2588 2589 if self._match_set((TokenType.FROM, TokenType.BETWEEN)): 2590 kind = self._prev.text.upper() 2591 start = self._parse_bitwise() 2592 self._match_texts(("TO", "AND")) 2593 end = self._parse_bitwise() 2594 expression: t.Optional[exp.Expression] = self.expression( 2595 exp.Tuple, expressions=[start, end] 2596 ) 2597 elif self._match_text_seq("CONTAINED", "IN"): 2598 kind = "CONTAINED IN" 2599 expression = self.expression( 2600 exp.Tuple, expressions=self._parse_wrapped_csv(self._parse_bitwise) 2601 ) 2602 elif self._match(TokenType.ALL): 2603 kind = "ALL" 2604 expression = None 2605 else: 2606 self._match_text_seq("AS", "OF") 2607 kind = "AS OF" 2608 expression = self._parse_type() 2609 2610 return self.expression(exp.Version, this=this, expression=expression, kind=kind) 2611 2612 def _parse_unnest(self, with_alias: bool = True) -> t.Optional[exp.Unnest]: 2613 if not self._match(TokenType.UNNEST): 2614 return None 2615 2616 expressions = self._parse_wrapped_csv(self._parse_type) 2617 ordinality = self._match_pair(TokenType.WITH, TokenType.ORDINALITY) 2618 2619 alias = self._parse_table_alias() if with_alias else None 2620 2621 if alias and self.UNNEST_COLUMN_ONLY: 2622 if alias.args.get("columns"): 2623 self.raise_error("Unexpected extra column alias in unnest.") 2624 2625 alias.set("columns", [alias.this]) 2626 alias.set("this", None) 2627 2628 offset = None 2629 if self._match_pair(TokenType.WITH, TokenType.OFFSET): 2630 self._match(TokenType.ALIAS) 2631 offset = self._parse_id_var() or exp.to_identifier("offset") 2632 2633 return self.expression( 2634 exp.Unnest, expressions=expressions, ordinality=ordinality, alias=alias, offset=offset 2635 ) 2636 2637 def _parse_derived_table_values(self) -> t.Optional[exp.Values]: 2638 is_derived = self._match_pair(TokenType.L_PAREN, TokenType.VALUES) 2639 if not is_derived and not self._match(TokenType.VALUES): 2640 return None 2641 2642 expressions = self._parse_csv(self._parse_value) 2643 alias = self._parse_table_alias() 2644 2645 if is_derived: 2646 self._match_r_paren() 2647 2648 return self.expression( 2649 exp.Values, expressions=expressions, alias=alias or self._parse_table_alias() 2650 ) 2651 2652 def _parse_table_sample(self, as_modifier: bool = False) -> t.Optional[exp.TableSample]: 2653 if not self._match(TokenType.TABLE_SAMPLE) and not ( 2654 as_modifier and self._match_text_seq("USING", "SAMPLE") 2655 ): 2656 return None 2657 2658 bucket_numerator = None 2659 bucket_denominator = None 2660 bucket_field = None 2661 percent = None 2662 rows = None 2663 size = None 2664 seed = None 2665 2666 kind = ( 2667 self._prev.text if self._prev.token_type == TokenType.TABLE_SAMPLE else "USING SAMPLE" 2668 ) 2669 method = self._parse_var(tokens=(TokenType.ROW,)) 2670 2671 self._match(TokenType.L_PAREN) 2672 2673 num = self._parse_number() 2674 2675 if self._match_text_seq("BUCKET"): 2676 bucket_numerator = self._parse_number() 2677 self._match_text_seq("OUT", "OF") 2678 bucket_denominator = bucket_denominator = self._parse_number() 2679 self._match(TokenType.ON) 2680 bucket_field = self._parse_field() 2681 elif self._match_set((TokenType.PERCENT, TokenType.MOD)): 2682 percent = num 2683 elif self._match(TokenType.ROWS): 2684 rows = num 2685 else: 2686 size = num 2687 2688 self._match(TokenType.R_PAREN) 2689 2690 if self._match(TokenType.L_PAREN): 2691 method = self._parse_var() 2692 seed = self._match(TokenType.COMMA) and self._parse_number() 2693 self._match_r_paren() 2694 elif self._match_texts(("SEED", "REPEATABLE")): 2695 seed = self._parse_wrapped(self._parse_number) 2696 2697 return self.expression( 2698 exp.TableSample, 2699 method=method, 2700 bucket_numerator=bucket_numerator, 2701 bucket_denominator=bucket_denominator, 2702 bucket_field=bucket_field, 2703 percent=percent, 2704 rows=rows, 2705 size=size, 2706 seed=seed, 2707 kind=kind, 2708 ) 2709 2710 def _parse_pivots(self) -> t.Optional[t.List[exp.Pivot]]: 2711 return list(iter(self._parse_pivot, None)) or None 2712 2713 def _parse_joins(self) -> t.Optional[t.List[exp.Join]]: 2714 return list(iter(self._parse_join, None)) or None 2715 2716 # https://duckdb.org/docs/sql/statements/pivot 2717 def _parse_simplified_pivot(self) -> exp.Pivot: 2718 def _parse_on() -> t.Optional[exp.Expression]: 2719 this = self._parse_bitwise() 2720 return self._parse_in(this) if self._match(TokenType.IN) else this 2721 2722 this = self._parse_table() 2723 expressions = self._match(TokenType.ON) and self._parse_csv(_parse_on) 2724 using = self._match(TokenType.USING) and self._parse_csv( 2725 lambda: self._parse_alias(self._parse_function()) 2726 ) 2727 group = self._parse_group() 2728 return self.expression( 2729 exp.Pivot, this=this, expressions=expressions, using=using, group=group 2730 ) 2731 2732 def _parse_pivot(self) -> t.Optional[exp.Pivot]: 2733 index = self._index 2734 include_nulls = None 2735 2736 if self._match(TokenType.PIVOT): 2737 unpivot = False 2738 elif self._match(TokenType.UNPIVOT): 2739 unpivot = True 2740 2741 # https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-unpivot.html#syntax 2742 if self._match_text_seq("INCLUDE", "NULLS"): 2743 include_nulls = True 2744 elif self._match_text_seq("EXCLUDE", "NULLS"): 2745 include_nulls = False 2746 else: 2747 return None 2748 2749 expressions = [] 2750 field = None 2751 2752 if not self._match(TokenType.L_PAREN): 2753 self._retreat(index) 2754 return None 2755 2756 if unpivot: 2757 expressions = self._parse_csv(self._parse_column) 2758 else: 2759 expressions = self._parse_csv(lambda: self._parse_alias(self._parse_function())) 2760 2761 if not expressions: 2762 self.raise_error("Failed to parse PIVOT's aggregation list") 2763 2764 if not self._match(TokenType.FOR): 2765 self.raise_error("Expecting FOR") 2766 2767 value = self._parse_column() 2768 2769 if not self._match(TokenType.IN): 2770 self.raise_error("Expecting IN") 2771 2772 field = self._parse_in(value, alias=True) 2773 2774 self._match_r_paren() 2775 2776 pivot = self.expression( 2777 exp.Pivot, 2778 expressions=expressions, 2779 field=field, 2780 unpivot=unpivot, 2781 include_nulls=include_nulls, 2782 ) 2783 2784 if not self._match_set((TokenType.PIVOT, TokenType.UNPIVOT), advance=False): 2785 pivot.set("alias", self._parse_table_alias()) 2786 2787 if not unpivot: 2788 names = self._pivot_column_names(t.cast(t.List[exp.Expression], expressions)) 2789 2790 columns: t.List[exp.Expression] = [] 2791 for fld in pivot.args["field"].expressions: 2792 field_name = fld.sql() if self.IDENTIFY_PIVOT_STRINGS else fld.alias_or_name 2793 for name in names: 2794 if self.PREFIXED_PIVOT_COLUMNS: 2795 name = f"{name}_{field_name}" if name else field_name 2796 else: 2797 name = f"{field_name}_{name}" if name else field_name 2798 2799 columns.append(exp.to_identifier(name)) 2800 2801 pivot.set("columns", columns) 2802 2803 return pivot 2804 2805 def _pivot_column_names(self, aggregations: t.List[exp.Expression]) -> t.List[str]: 2806 return [agg.alias for agg in aggregations] 2807 2808 def _parse_where(self, skip_where_token: bool = False) -> t.Optional[exp.Where]: 2809 if not skip_where_token and not self._match(TokenType.WHERE): 2810 return None 2811 2812 return self.expression( 2813 exp.Where, comments=self._prev_comments, this=self._parse_conjunction() 2814 ) 2815 2816 def _parse_group(self, skip_group_by_token: bool = False) -> t.Optional[exp.Group]: 2817 if not skip_group_by_token and not self._match(TokenType.GROUP_BY): 2818 return None 2819 2820 elements = defaultdict(list) 2821 2822 if self._match(TokenType.ALL): 2823 return self.expression(exp.Group, all=True) 2824 2825 while True: 2826 expressions = self._parse_csv(self._parse_conjunction) 2827 if expressions: 2828 elements["expressions"].extend(expressions) 2829 2830 grouping_sets = self._parse_grouping_sets() 2831 if grouping_sets: 2832 elements["grouping_sets"].extend(grouping_sets) 2833 2834 rollup = None 2835 cube = None 2836 totals = None 2837 2838 with_ = self._match(TokenType.WITH) 2839 if self._match(TokenType.ROLLUP): 2840 rollup = with_ or self._parse_wrapped_csv(self._parse_column) 2841 elements["rollup"].extend(ensure_list(rollup)) 2842 2843 if self._match(TokenType.CUBE): 2844 cube = with_ or self._parse_wrapped_csv(self._parse_column) 2845 elements["cube"].extend(ensure_list(cube)) 2846 2847 if self._match_text_seq("TOTALS"): 2848 totals = True 2849 elements["totals"] = True # type: ignore 2850 2851 if not (grouping_sets or rollup or cube or totals): 2852 break 2853 2854 return self.expression(exp.Group, **elements) # type: ignore 2855 2856 def _parse_grouping_sets(self) -> t.Optional[t.List[exp.Expression]]: 2857 if not self._match(TokenType.GROUPING_SETS): 2858 return None 2859 2860 return self._parse_wrapped_csv(self._parse_grouping_set) 2861 2862 def _parse_grouping_set(self) -> t.Optional[exp.Expression]: 2863 if self._match(TokenType.L_PAREN): 2864 grouping_set = self._parse_csv(self._parse_column) 2865 self._match_r_paren() 2866 return self.expression(exp.Tuple, expressions=grouping_set) 2867 2868 return self._parse_column() 2869 2870 def _parse_having(self, skip_having_token: bool = False) -> t.Optional[exp.Having]: 2871 if not skip_having_token and not self._match(TokenType.HAVING): 2872 return None 2873 return self.expression(exp.Having, this=self._parse_conjunction()) 2874 2875 def _parse_qualify(self) -> t.Optional[exp.Qualify]: 2876 if not self._match(TokenType.QUALIFY): 2877 return None 2878 return self.expression(exp.Qualify, this=self._parse_conjunction()) 2879 2880 def _parse_connect(self, skip_start_token: bool = False) -> t.Optional[exp.Connect]: 2881 if skip_start_token: 2882 start = None 2883 elif self._match(TokenType.START_WITH): 2884 start = self._parse_conjunction() 2885 else: 2886 return None 2887 2888 self._match(TokenType.CONNECT_BY) 2889 self.NO_PAREN_FUNCTION_PARSERS["PRIOR"] = lambda self: self.expression( 2890 exp.Prior, this=self._parse_bitwise() 2891 ) 2892 connect = self._parse_conjunction() 2893 self.NO_PAREN_FUNCTION_PARSERS.pop("PRIOR") 2894 return self.expression(exp.Connect, start=start, connect=connect) 2895 2896 def _parse_order( 2897 self, this: t.Optional[exp.Expression] = None, skip_order_token: bool = False 2898 ) -> t.Optional[exp.Expression]: 2899 if not skip_order_token and not self._match(TokenType.ORDER_BY): 2900 return this 2901 2902 return self.expression( 2903 exp.Order, this=this, expressions=self._parse_csv(self._parse_ordered) 2904 ) 2905 2906 def _parse_sort(self, exp_class: t.Type[E], token: TokenType) -> t.Optional[E]: 2907 if not self._match(token): 2908 return None 2909 return self.expression(exp_class, expressions=self._parse_csv(self._parse_ordered)) 2910 2911 def _parse_ordered(self) -> exp.Ordered: 2912 this = self._parse_conjunction() 2913 self._match(TokenType.ASC) 2914 2915 is_desc = self._match(TokenType.DESC) 2916 is_nulls_first = self._match_text_seq("NULLS", "FIRST") 2917 is_nulls_last = self._match_text_seq("NULLS", "LAST") 2918 desc = is_desc or False 2919 asc = not desc 2920 nulls_first = is_nulls_first or False 2921 explicitly_null_ordered = is_nulls_first or is_nulls_last 2922 2923 if ( 2924 not explicitly_null_ordered 2925 and ( 2926 (asc and self.NULL_ORDERING == "nulls_are_small") 2927 or (desc and self.NULL_ORDERING != "nulls_are_small") 2928 ) 2929 and self.NULL_ORDERING != "nulls_are_last" 2930 ): 2931 nulls_first = True 2932 2933 return self.expression(exp.Ordered, this=this, desc=desc, nulls_first=nulls_first) 2934 2935 def _parse_limit( 2936 self, this: t.Optional[exp.Expression] = None, top: bool = False 2937 ) -> t.Optional[exp.Expression]: 2938 if self._match(TokenType.TOP if top else TokenType.LIMIT): 2939 comments = self._prev_comments 2940 if top: 2941 limit_paren = self._match(TokenType.L_PAREN) 2942 expression = self._parse_number() 2943 2944 if limit_paren: 2945 self._match_r_paren() 2946 else: 2947 expression = self._parse_term() 2948 2949 if self._match(TokenType.COMMA): 2950 offset = expression 2951 expression = self._parse_term() 2952 else: 2953 offset = None 2954 2955 limit_exp = self.expression( 2956 exp.Limit, this=this, expression=expression, offset=offset, comments=comments 2957 ) 2958 2959 return limit_exp 2960 2961 if self._match(TokenType.FETCH): 2962 direction = self._match_set((TokenType.FIRST, TokenType.NEXT)) 2963 direction = self._prev.text if direction else "FIRST" 2964 2965 count = self._parse_number() 2966 percent = self._match(TokenType.PERCENT) 2967 2968 self._match_set((TokenType.ROW, TokenType.ROWS)) 2969 2970 only = self._match_text_seq("ONLY") 2971 with_ties = self._match_text_seq("WITH", "TIES") 2972 2973 if only and with_ties: 2974 self.raise_error("Cannot specify both ONLY and WITH TIES in FETCH clause") 2975 2976 return self.expression( 2977 exp.Fetch, 2978 direction=direction, 2979 count=count, 2980 percent=percent, 2981 with_ties=with_ties, 2982 ) 2983 2984 return this 2985 2986 def _parse_offset(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]: 2987 if not self._match(TokenType.OFFSET): 2988 return this 2989 2990 count = self._parse_term() 2991 self._match_set((TokenType.ROW, TokenType.ROWS)) 2992 return self.expression(exp.Offset, this=this, expression=count) 2993 2994 def _parse_locks(self) -> t.List[exp.Lock]: 2995 locks = [] 2996 while True: 2997 if self._match_text_seq("FOR", "UPDATE"): 2998 update = True 2999 elif self._match_text_seq("FOR", "SHARE") or self._match_text_seq( 3000 "LOCK", "IN", "SHARE", "MODE" 3001 ): 3002 update = False 3003 else: 3004 break 3005 3006 expressions = None 3007 if self._match_text_seq("OF"): 3008 expressions = self._parse_csv(lambda: self._parse_table(schema=True)) 3009 3010 wait: t.Optional[bool | exp.Expression] = None 3011 if self._match_text_seq("NOWAIT"): 3012 wait = True 3013 elif self._match_text_seq("WAIT"): 3014 wait = self._parse_primary() 3015 elif self._match_text_seq("SKIP", "LOCKED"): 3016 wait = False 3017 3018 locks.append( 3019 self.expression(exp.Lock, update=update, expressions=expressions, wait=wait) 3020 ) 3021 3022 return locks 3023 3024 def _parse_set_operations(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3025 if not self._match_set(self.SET_OPERATIONS): 3026 return this 3027 3028 token_type = self._prev.token_type 3029 3030 if token_type == TokenType.UNION: 3031 expression = exp.Union 3032 elif token_type == TokenType.EXCEPT: 3033 expression = exp.Except 3034 else: 3035 expression = exp.Intersect 3036 3037 return self.expression( 3038 expression, 3039 this=this, 3040 distinct=self._match(TokenType.DISTINCT) or not self._match(TokenType.ALL), 3041 by_name=self._match_text_seq("BY", "NAME"), 3042 expression=self._parse_set_operations(self._parse_select(nested=True)), 3043 ) 3044 3045 def _parse_expression(self) -> t.Optional[exp.Expression]: 3046 return self._parse_alias(self._parse_conjunction()) 3047 3048 def _parse_conjunction(self) -> t.Optional[exp.Expression]: 3049 return self._parse_tokens(self._parse_equality, self.CONJUNCTION) 3050 3051 def _parse_equality(self) -> t.Optional[exp.Expression]: 3052 return self._parse_tokens(self._parse_comparison, self.EQUALITY) 3053 3054 def _parse_comparison(self) -> t.Optional[exp.Expression]: 3055 return self._parse_tokens(self._parse_range, self.COMPARISON) 3056 3057 def _parse_range(self) -> t.Optional[exp.Expression]: 3058 this = self._parse_bitwise() 3059 negate = self._match(TokenType.NOT) 3060 3061 if self._match_set(self.RANGE_PARSERS): 3062 expression = self.RANGE_PARSERS[self._prev.token_type](self, this) 3063 if not expression: 3064 return this 3065 3066 this = expression 3067 elif self._match(TokenType.ISNULL): 3068 this = self.expression(exp.Is, this=this, expression=exp.Null()) 3069 3070 # Postgres supports ISNULL and NOTNULL for conditions. 3071 # https://blog.andreiavram.ro/postgresql-null-composite-type/ 3072 if self._match(TokenType.NOTNULL): 3073 this = self.expression(exp.Is, this=this, expression=exp.Null()) 3074 this = self.expression(exp.Not, this=this) 3075 3076 if negate: 3077 this = self.expression(exp.Not, this=this) 3078 3079 if self._match(TokenType.IS): 3080 this = self._parse_is(this) 3081 3082 return this 3083 3084 def _parse_is(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3085 index = self._index - 1 3086 negate = self._match(TokenType.NOT) 3087 3088 if self._match_text_seq("DISTINCT", "FROM"): 3089 klass = exp.NullSafeEQ if negate else exp.NullSafeNEQ 3090 return self.expression(klass, this=this, expression=self._parse_expression()) 3091 3092 expression = self._parse_null() or self._parse_boolean() 3093 if not expression: 3094 self._retreat(index) 3095 return None 3096 3097 this = self.expression(exp.Is, this=this, expression=expression) 3098 return self.expression(exp.Not, this=this) if negate else this 3099 3100 def _parse_in(self, this: t.Optional[exp.Expression], alias: bool = False) -> exp.In: 3101 unnest = self._parse_unnest(with_alias=False) 3102 if unnest: 3103 this = self.expression(exp.In, this=this, unnest=unnest) 3104 elif self._match(TokenType.L_PAREN): 3105 expressions = self._parse_csv(lambda: self._parse_select_or_expression(alias=alias)) 3106 3107 if len(expressions) == 1 and isinstance(expressions[0], exp.Subqueryable): 3108 this = self.expression(exp.In, this=this, query=expressions[0]) 3109 else: 3110 this = self.expression(exp.In, this=this, expressions=expressions) 3111 3112 self._match_r_paren(this) 3113 else: 3114 this = self.expression(exp.In, this=this, field=self._parse_field()) 3115 3116 return this 3117 3118 def _parse_between(self, this: exp.Expression) -> exp.Between: 3119 low = self._parse_bitwise() 3120 self._match(TokenType.AND) 3121 high = self._parse_bitwise() 3122 return self.expression(exp.Between, this=this, low=low, high=high) 3123 3124 def _parse_escape(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3125 if not self._match(TokenType.ESCAPE): 3126 return this 3127 return self.expression(exp.Escape, this=this, expression=self._parse_string()) 3128 3129 def _parse_interval(self) -> t.Optional[exp.Interval]: 3130 index = self._index 3131 3132 if not self._match(TokenType.INTERVAL): 3133 return None 3134 3135 if self._match(TokenType.STRING, advance=False): 3136 this = self._parse_primary() 3137 else: 3138 this = self._parse_term() 3139 3140 if not this: 3141 self._retreat(index) 3142 return None 3143 3144 unit = self._parse_function() or self._parse_var(any_token=True) 3145 3146 # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse 3147 # each INTERVAL expression into this canonical form so it's easy to transpile 3148 if this and this.is_number: 3149 this = exp.Literal.string(this.name) 3150 elif this and this.is_string: 3151 parts = this.name.split() 3152 3153 if len(parts) == 2: 3154 if unit: 3155 # this is not actually a unit, it's something else 3156 unit = None 3157 self._retreat(self._index - 1) 3158 else: 3159 this = exp.Literal.string(parts[0]) 3160 unit = self.expression(exp.Var, this=parts[1]) 3161 3162 return self.expression(exp.Interval, this=this, unit=unit) 3163 3164 def _parse_bitwise(self) -> t.Optional[exp.Expression]: 3165 this = self._parse_term() 3166 3167 while True: 3168 if self._match_set(self.BITWISE): 3169 this = self.expression( 3170 self.BITWISE[self._prev.token_type], 3171 this=this, 3172 expression=self._parse_term(), 3173 ) 3174 elif self._match(TokenType.DQMARK): 3175 this = self.expression(exp.Coalesce, this=this, expressions=self._parse_term()) 3176 elif self._match_pair(TokenType.LT, TokenType.LT): 3177 this = self.expression( 3178 exp.BitwiseLeftShift, this=this, expression=self._parse_term() 3179 ) 3180 elif self._match_pair(TokenType.GT, TokenType.GT): 3181 this = self.expression( 3182 exp.BitwiseRightShift, this=this, expression=self._parse_term() 3183 ) 3184 else: 3185 break 3186 3187 return this 3188 3189 def _parse_term(self) -> t.Optional[exp.Expression]: 3190 return self._parse_tokens(self._parse_factor, self.TERM) 3191 3192 def _parse_factor(self) -> t.Optional[exp.Expression]: 3193 return self._parse_tokens(self._parse_unary, self.FACTOR) 3194 3195 def _parse_unary(self) -> t.Optional[exp.Expression]: 3196 if self._match_set(self.UNARY_PARSERS): 3197 return self.UNARY_PARSERS[self._prev.token_type](self) 3198 return self._parse_at_time_zone(self._parse_type()) 3199 3200 def _parse_type(self) -> t.Optional[exp.Expression]: 3201 interval = self._parse_interval() 3202 if interval: 3203 return interval 3204 3205 index = self._index 3206 data_type = self._parse_types(check_func=True, allow_identifiers=False) 3207 this = self._parse_column() 3208 3209 if data_type: 3210 if isinstance(this, exp.Literal): 3211 parser = self.TYPE_LITERAL_PARSERS.get(data_type.this) 3212 if parser: 3213 return parser(self, this, data_type) 3214 return self.expression(exp.Cast, this=this, to=data_type) 3215 if not data_type.expressions: 3216 self._retreat(index) 3217 return self._parse_column() 3218 return self._parse_column_ops(data_type) 3219 3220 return this 3221 3222 def _parse_type_size(self) -> t.Optional[exp.DataTypeParam]: 3223 this = self._parse_type() 3224 if not this: 3225 return None 3226 3227 return self.expression( 3228 exp.DataTypeParam, this=this, expression=self._parse_var(any_token=True) 3229 ) 3230 3231 def _parse_types( 3232 self, check_func: bool = False, schema: bool = False, allow_identifiers: bool = True 3233 ) -> t.Optional[exp.Expression]: 3234 index = self._index 3235 3236 prefix = self._match_text_seq("SYSUDTLIB", ".") 3237 3238 if not self._match_set(self.TYPE_TOKENS): 3239 identifier = allow_identifiers and self._parse_id_var( 3240 any_token=False, tokens=(TokenType.VAR,) 3241 ) 3242 3243 if identifier: 3244 tokens = self._tokenizer.tokenize(identifier.name) 3245 3246 if len(tokens) != 1: 3247 self.raise_error("Unexpected identifier", self._prev) 3248 3249 if tokens[0].token_type in self.TYPE_TOKENS: 3250 self._prev = tokens[0] 3251 elif self.SUPPORTS_USER_DEFINED_TYPES: 3252 return identifier 3253 else: 3254 return None 3255 else: 3256 return None 3257 3258 type_token = self._prev.token_type 3259 3260 if type_token == TokenType.PSEUDO_TYPE: 3261 return self.expression(exp.PseudoType, this=self._prev.text) 3262 3263 nested = type_token in self.NESTED_TYPE_TOKENS 3264 is_struct = type_token in self.STRUCT_TYPE_TOKENS 3265 expressions = None 3266 maybe_func = False 3267 3268 if self._match(TokenType.L_PAREN): 3269 if is_struct: 3270 expressions = self._parse_csv(self._parse_struct_types) 3271 elif nested: 3272 expressions = self._parse_csv( 3273 lambda: self._parse_types( 3274 check_func=check_func, schema=schema, allow_identifiers=allow_identifiers 3275 ) 3276 ) 3277 elif type_token in self.ENUM_TYPE_TOKENS: 3278 expressions = self._parse_csv(self._parse_equality) 3279 else: 3280 expressions = self._parse_csv(self._parse_type_size) 3281 3282 if not expressions or not self._match(TokenType.R_PAREN): 3283 self._retreat(index) 3284 return None 3285 3286 maybe_func = True 3287 3288 this: t.Optional[exp.Expression] = None 3289 values: t.Optional[t.List[exp.Expression]] = None 3290 3291 if nested and self._match(TokenType.LT): 3292 if is_struct: 3293 expressions = self._parse_csv(self._parse_struct_types) 3294 else: 3295 expressions = self._parse_csv( 3296 lambda: self._parse_types( 3297 check_func=check_func, schema=schema, allow_identifiers=allow_identifiers 3298 ) 3299 ) 3300 3301 if not self._match(TokenType.GT): 3302 self.raise_error("Expecting >") 3303 3304 if self._match_set((TokenType.L_BRACKET, TokenType.L_PAREN)): 3305 values = self._parse_csv(self._parse_conjunction) 3306 self._match_set((TokenType.R_BRACKET, TokenType.R_PAREN)) 3307 3308 if type_token in self.TIMESTAMPS: 3309 if self._match_text_seq("WITH", "TIME", "ZONE"): 3310 maybe_func = False 3311 tz_type = ( 3312 exp.DataType.Type.TIMETZ 3313 if type_token in self.TIMES 3314 else exp.DataType.Type.TIMESTAMPTZ 3315 ) 3316 this = exp.DataType(this=tz_type, expressions=expressions) 3317 elif self._match_text_seq("WITH", "LOCAL", "TIME", "ZONE"): 3318 maybe_func = False 3319 this = exp.DataType(this=exp.DataType.Type.TIMESTAMPLTZ, expressions=expressions) 3320 elif self._match_text_seq("WITHOUT", "TIME", "ZONE"): 3321 maybe_func = False 3322 elif type_token == TokenType.INTERVAL: 3323 if self._match_text_seq("YEAR", "TO", "MONTH"): 3324 span: t.Optional[t.List[exp.Expression]] = [exp.IntervalYearToMonthSpan()] 3325 elif self._match_text_seq("DAY", "TO", "SECOND"): 3326 span = [exp.IntervalDayToSecondSpan()] 3327 else: 3328 span = None 3329 3330 unit = not span and self._parse_var() 3331 if not unit: 3332 this = self.expression( 3333 exp.DataType, this=exp.DataType.Type.INTERVAL, expressions=span 3334 ) 3335 else: 3336 this = self.expression(exp.Interval, unit=unit) 3337 3338 if maybe_func and check_func: 3339 index2 = self._index 3340 peek = self._parse_string() 3341 3342 if not peek: 3343 self._retreat(index) 3344 return None 3345 3346 self._retreat(index2) 3347 3348 if not this: 3349 this = exp.DataType( 3350 this=exp.DataType.Type[type_token.value], 3351 expressions=expressions, 3352 nested=nested, 3353 values=values, 3354 prefix=prefix, 3355 ) 3356 3357 while self._match_pair(TokenType.L_BRACKET, TokenType.R_BRACKET): 3358 this = exp.DataType(this=exp.DataType.Type.ARRAY, expressions=[this], nested=True) 3359 3360 return this 3361 3362 def _parse_struct_types(self) -> t.Optional[exp.Expression]: 3363 this = self._parse_type() or self._parse_id_var() 3364 self._match(TokenType.COLON) 3365 return self._parse_column_def(this) 3366 3367 def _parse_at_time_zone(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3368 if not self._match_text_seq("AT", "TIME", "ZONE"): 3369 return this 3370 return self.expression(exp.AtTimeZone, this=this, zone=self._parse_unary()) 3371 3372 def _parse_column(self) -> t.Optional[exp.Expression]: 3373 this = self._parse_field() 3374 if isinstance(this, exp.Identifier): 3375 this = self.expression(exp.Column, this=this) 3376 elif not this: 3377 return self._parse_bracket(this) 3378 return self._parse_column_ops(this) 3379 3380 def _parse_column_ops(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3381 this = self._parse_bracket(this) 3382 3383 while self._match_set(self.COLUMN_OPERATORS): 3384 op_token = self._prev.token_type 3385 op = self.COLUMN_OPERATORS.get(op_token) 3386 3387 if op_token == TokenType.DCOLON: 3388 field = self._parse_types() 3389 if not field: 3390 self.raise_error("Expected type") 3391 elif op and self._curr: 3392 self._advance() 3393 value = self._prev.text 3394 field = ( 3395 exp.Literal.number(value) 3396 if self._prev.token_type == TokenType.NUMBER 3397 else exp.Literal.string(value) 3398 ) 3399 else: 3400 field = self._parse_field(anonymous_func=True, any_token=True) 3401 3402 if isinstance(field, exp.Func): 3403 # bigquery allows function calls like x.y.count(...) 3404 # SAFE.SUBSTR(...) 3405 # https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-reference#function_call_rules 3406 this = self._replace_columns_with_dots(this) 3407 3408 if op: 3409 this = op(self, this, field) 3410 elif isinstance(this, exp.Column) and not this.args.get("catalog"): 3411 this = self.expression( 3412 exp.Column, 3413 this=field, 3414 table=this.this, 3415 db=this.args.get("table"), 3416 catalog=this.args.get("db"), 3417 ) 3418 else: 3419 this = self.expression(exp.Dot, this=this, expression=field) 3420 this = self._parse_bracket(this) 3421 return this 3422 3423 def _parse_primary(self) -> t.Optional[exp.Expression]: 3424 if self._match_set(self.PRIMARY_PARSERS): 3425 token_type = self._prev.token_type 3426 primary = self.PRIMARY_PARSERS[token_type](self, self._prev) 3427 3428 if token_type == TokenType.STRING: 3429 expressions = [primary] 3430 while self._match(TokenType.STRING): 3431 expressions.append(exp.Literal.string(self._prev.text)) 3432 3433 if len(expressions) > 1: 3434 return self.expression(exp.Concat, expressions=expressions) 3435 3436 return primary 3437 3438 if self._match_pair(TokenType.DOT, TokenType.NUMBER): 3439 return exp.Literal.number(f"0.{self._prev.text}") 3440 3441 if self._match(TokenType.L_PAREN): 3442 comments = self._prev_comments 3443 query = self._parse_select() 3444 3445 if query: 3446 expressions = [query] 3447 else: 3448 expressions = self._parse_expressions() 3449 3450 this = self._parse_query_modifiers(seq_get(expressions, 0)) 3451 3452 if isinstance(this, exp.Subqueryable): 3453 this = self._parse_set_operations( 3454 self._parse_subquery(this=this, parse_alias=False) 3455 ) 3456 elif len(expressions) > 1: 3457 this = self.expression(exp.Tuple, expressions=expressions) 3458 else: 3459 this = self.expression(exp.Paren, this=self._parse_set_operations(this)) 3460 3461 if this: 3462 this.add_comments(comments) 3463 3464 self._match_r_paren(expression=this) 3465 return this 3466 3467 return None 3468 3469 def _parse_field( 3470 self, 3471 any_token: bool = False, 3472 tokens: t.Optional[t.Collection[TokenType]] = None, 3473 anonymous_func: bool = False, 3474 ) -> t.Optional[exp.Expression]: 3475 return ( 3476 self._parse_primary() 3477 or self._parse_function(anonymous=anonymous_func) 3478 or self._parse_id_var(any_token=any_token, tokens=tokens) 3479 ) 3480 3481 def _parse_function( 3482 self, 3483 functions: t.Optional[t.Dict[str, t.Callable]] = None, 3484 anonymous: bool = False, 3485 optional_parens: bool = True, 3486 ) -> t.Optional[exp.Expression]: 3487 if not self._curr: 3488 return None 3489 3490 token_type = self._curr.token_type 3491 this = self._curr.text 3492 upper = this.upper() 3493 3494 parser = self.NO_PAREN_FUNCTION_PARSERS.get(upper) 3495 if optional_parens and parser and token_type not in self.INVALID_FUNC_NAME_TOKENS: 3496 self._advance() 3497 return parser(self) 3498 3499 if not self._next or self._next.token_type != TokenType.L_PAREN: 3500 if optional_parens and token_type in self.NO_PAREN_FUNCTIONS: 3501 self._advance() 3502 return self.expression(self.NO_PAREN_FUNCTIONS[token_type]) 3503 3504 return None 3505 3506 if token_type not in self.FUNC_TOKENS: 3507 return None 3508 3509 self._advance(2) 3510 3511 parser = self.FUNCTION_PARSERS.get(upper) 3512 if parser and not anonymous: 3513 this = parser(self) 3514 else: 3515 subquery_predicate = self.SUBQUERY_PREDICATES.get(token_type) 3516 3517 if subquery_predicate and self._curr.token_type in (TokenType.SELECT, TokenType.WITH): 3518 this = self.expression(subquery_predicate, this=self._parse_select()) 3519 self._match_r_paren() 3520 return this 3521 3522 if functions is None: 3523 functions = self.FUNCTIONS 3524 3525 function = functions.get(upper) 3526 3527 alias = upper in self.FUNCTIONS_WITH_ALIASED_ARGS 3528 args = self._parse_csv(lambda: self._parse_lambda(alias=alias)) 3529 3530 if function and not anonymous: 3531 func = self.validate_expression(function(args), args) 3532 if not self.NORMALIZE_FUNCTIONS: 3533 func.meta["name"] = this 3534 this = func 3535 else: 3536 this = self.expression(exp.Anonymous, this=this, expressions=args) 3537 3538 self._match_r_paren(this) 3539 return self._parse_window(this) 3540 3541 def _parse_function_parameter(self) -> t.Optional[exp.Expression]: 3542 return self._parse_column_def(self._parse_id_var()) 3543 3544 def _parse_user_defined_function( 3545 self, kind: t.Optional[TokenType] = None 3546 ) -> t.Optional[exp.Expression]: 3547 this = self._parse_id_var() 3548 3549 while self._match(TokenType.DOT): 3550 this = self.expression(exp.Dot, this=this, expression=self._parse_id_var()) 3551 3552 if not self._match(TokenType.L_PAREN): 3553 return this 3554 3555 expressions = self._parse_csv(self._parse_function_parameter) 3556 self._match_r_paren() 3557 return self.expression( 3558 exp.UserDefinedFunction, this=this, expressions=expressions, wrapped=True 3559 ) 3560 3561 def _parse_introducer(self, token: Token) -> exp.Introducer | exp.Identifier: 3562 literal = self._parse_primary() 3563 if literal: 3564 return self.expression(exp.Introducer, this=token.text, expression=literal) 3565 3566 return self.expression(exp.Identifier, this=token.text) 3567 3568 def _parse_session_parameter(self) -> exp.SessionParameter: 3569 kind = None 3570 this = self._parse_id_var() or self._parse_primary() 3571 3572 if this and self._match(TokenType.DOT): 3573 kind = this.name 3574 this = self._parse_var() or self._parse_primary() 3575 3576 return self.expression(exp.SessionParameter, this=this, kind=kind) 3577 3578 def _parse_lambda(self, alias: bool = False) -> t.Optional[exp.Expression]: 3579 index = self._index 3580 3581 if self._match(TokenType.L_PAREN): 3582 expressions = t.cast( 3583 t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_id_var) 3584 ) 3585 3586 if not self._match(TokenType.R_PAREN): 3587 self._retreat(index) 3588 else: 3589 expressions = [self._parse_id_var()] 3590 3591 if self._match_set(self.LAMBDAS): 3592 return self.LAMBDAS[self._prev.token_type](self, expressions) 3593 3594 self._retreat(index) 3595 3596 this: t.Optional[exp.Expression] 3597 3598 if self._match(TokenType.DISTINCT): 3599 this = self.expression( 3600 exp.Distinct, expressions=self._parse_csv(self._parse_conjunction) 3601 ) 3602 else: 3603 this = self._parse_select_or_expression(alias=alias) 3604 3605 return self._parse_limit(self._parse_order(self._parse_respect_or_ignore_nulls(this))) 3606 3607 def _parse_schema(self, this: t.Optional[exp.Expression] = None) -> t.Optional[exp.Expression]: 3608 index = self._index 3609 3610 if not self.errors: 3611 try: 3612 if self._parse_select(nested=True): 3613 return this 3614 except ParseError: 3615 pass 3616 finally: 3617 self.errors.clear() 3618 self._retreat(index) 3619 3620 if not self._match(TokenType.L_PAREN): 3621 return this 3622 3623 args = self._parse_csv(lambda: self._parse_constraint() or self._parse_field_def()) 3624 3625 self._match_r_paren() 3626 return self.expression(exp.Schema, this=this, expressions=args) 3627 3628 def _parse_field_def(self) -> t.Optional[exp.Expression]: 3629 return self._parse_column_def(self._parse_field(any_token=True)) 3630 3631 def _parse_column_def(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3632 # column defs are not really columns, they're identifiers 3633 if isinstance(this, exp.Column): 3634 this = this.this 3635 3636 kind = self._parse_types(schema=True) 3637 3638 if self._match_text_seq("FOR", "ORDINALITY"): 3639 return self.expression(exp.ColumnDef, this=this, ordinality=True) 3640 3641 constraints: t.List[exp.Expression] = [] 3642 3643 if not kind and self._match(TokenType.ALIAS): 3644 constraints.append( 3645 self.expression( 3646 exp.ComputedColumnConstraint, 3647 this=self._parse_conjunction(), 3648 persisted=self._match_text_seq("PERSISTED"), 3649 not_null=self._match_pair(TokenType.NOT, TokenType.NULL), 3650 ) 3651 ) 3652 3653 while True: 3654 constraint = self._parse_column_constraint() 3655 if not constraint: 3656 break 3657 constraints.append(constraint) 3658 3659 if not kind and not constraints: 3660 return this 3661 3662 return self.expression(exp.ColumnDef, this=this, kind=kind, constraints=constraints) 3663 3664 def _parse_auto_increment( 3665 self, 3666 ) -> exp.GeneratedAsIdentityColumnConstraint | exp.AutoIncrementColumnConstraint: 3667 start = None 3668 increment = None 3669 3670 if self._match(TokenType.L_PAREN, advance=False): 3671 args = self._parse_wrapped_csv(self._parse_bitwise) 3672 start = seq_get(args, 0) 3673 increment = seq_get(args, 1) 3674 elif self._match_text_seq("START"): 3675 start = self._parse_bitwise() 3676 self._match_text_seq("INCREMENT") 3677 increment = self._parse_bitwise() 3678 3679 if start and increment: 3680 return exp.GeneratedAsIdentityColumnConstraint(start=start, increment=increment) 3681 3682 return exp.AutoIncrementColumnConstraint() 3683 3684 def _parse_compress(self) -> exp.CompressColumnConstraint: 3685 if self._match(TokenType.L_PAREN, advance=False): 3686 return self.expression( 3687 exp.CompressColumnConstraint, this=self._parse_wrapped_csv(self._parse_bitwise) 3688 ) 3689 3690 return self.expression(exp.CompressColumnConstraint, this=self._parse_bitwise()) 3691 3692 def _parse_generated_as_identity(self) -> exp.GeneratedAsIdentityColumnConstraint: 3693 if self._match_text_seq("BY", "DEFAULT"): 3694 on_null = self._match_pair(TokenType.ON, TokenType.NULL) 3695 this = self.expression( 3696 exp.GeneratedAsIdentityColumnConstraint, this=False, on_null=on_null 3697 ) 3698 else: 3699 self._match_text_seq("ALWAYS") 3700 this = self.expression(exp.GeneratedAsIdentityColumnConstraint, this=True) 3701 3702 self._match(TokenType.ALIAS) 3703 identity = self._match_text_seq("IDENTITY") 3704 3705 if self._match(TokenType.L_PAREN): 3706 if self._match(TokenType.START_WITH): 3707 this.set("start", self._parse_bitwise()) 3708 if self._match_text_seq("INCREMENT", "BY"): 3709 this.set("increment", self._parse_bitwise()) 3710 if self._match_text_seq("MINVALUE"): 3711 this.set("minvalue", self._parse_bitwise()) 3712 if self._match_text_seq("MAXVALUE"): 3713 this.set("maxvalue", self._parse_bitwise()) 3714 3715 if self._match_text_seq("CYCLE"): 3716 this.set("cycle", True) 3717 elif self._match_text_seq("NO", "CYCLE"): 3718 this.set("cycle", False) 3719 3720 if not identity: 3721 this.set("expression", self._parse_bitwise()) 3722 3723 self._match_r_paren() 3724 3725 return this 3726 3727 def _parse_inline(self) -> exp.InlineLengthColumnConstraint: 3728 self._match_text_seq("LENGTH") 3729 return self.expression(exp.InlineLengthColumnConstraint, this=self._parse_bitwise()) 3730 3731 def _parse_not_constraint( 3732 self, 3733 ) -> t.Optional[exp.Expression]: 3734 if self._match_text_seq("NULL"): 3735 return self.expression(exp.NotNullColumnConstraint) 3736 if self._match_text_seq("CASESPECIFIC"): 3737 return self.expression(exp.CaseSpecificColumnConstraint, not_=True) 3738 if self._match_text_seq("FOR", "REPLICATION"): 3739 return self.expression(exp.NotForReplicationColumnConstraint) 3740 return None 3741 3742 def _parse_column_constraint(self) -> t.Optional[exp.Expression]: 3743 if self._match(TokenType.CONSTRAINT): 3744 this = self._parse_id_var() 3745 else: 3746 this = None 3747 3748 if self._match_texts(self.CONSTRAINT_PARSERS): 3749 return self.expression( 3750 exp.ColumnConstraint, 3751 this=this, 3752 kind=self.CONSTRAINT_PARSERS[self._prev.text.upper()](self), 3753 ) 3754 3755 return this 3756 3757 def _parse_constraint(self) -> t.Optional[exp.Expression]: 3758 if not self._match(TokenType.CONSTRAINT): 3759 return self._parse_unnamed_constraint(constraints=self.SCHEMA_UNNAMED_CONSTRAINTS) 3760 3761 this = self._parse_id_var() 3762 expressions = [] 3763 3764 while True: 3765 constraint = self._parse_unnamed_constraint() or self._parse_function() 3766 if not constraint: 3767 break 3768 expressions.append(constraint) 3769 3770 return self.expression(exp.Constraint, this=this, expressions=expressions) 3771 3772 def _parse_unnamed_constraint( 3773 self, constraints: t.Optional[t.Collection[str]] = None 3774 ) -> t.Optional[exp.Expression]: 3775 if not self._match_texts(constraints or self.CONSTRAINT_PARSERS): 3776 return None 3777 3778 constraint = self._prev.text.upper() 3779 if constraint not in self.CONSTRAINT_PARSERS: 3780 self.raise_error(f"No parser found for schema constraint {constraint}.") 3781 3782 return self.CONSTRAINT_PARSERS[constraint](self) 3783 3784 def _parse_unique(self) -> exp.UniqueColumnConstraint: 3785 self._match_text_seq("KEY") 3786 return self.expression( 3787 exp.UniqueColumnConstraint, this=self._parse_schema(self._parse_id_var(any_token=False)) 3788 ) 3789 3790 def _parse_key_constraint_options(self) -> t.List[str]: 3791 options = [] 3792 while True: 3793 if not self._curr: 3794 break 3795 3796 if self._match(TokenType.ON): 3797 action = None 3798 on = self._advance_any() and self._prev.text 3799 3800 if self._match_text_seq("NO", "ACTION"): 3801 action = "NO ACTION" 3802 elif self._match_text_seq("CASCADE"): 3803 action = "CASCADE" 3804 elif self._match_pair(TokenType.SET, TokenType.NULL): 3805 action = "SET NULL" 3806 elif self._match_pair(TokenType.SET, TokenType.DEFAULT): 3807 action = "SET DEFAULT" 3808 else: 3809 self.raise_error("Invalid key constraint") 3810 3811 options.append(f"ON {on} {action}") 3812 elif self._match_text_seq("NOT", "ENFORCED"): 3813 options.append("NOT ENFORCED") 3814 elif self._match_text_seq("DEFERRABLE"): 3815 options.append("DEFERRABLE") 3816 elif self._match_text_seq("INITIALLY", "DEFERRED"): 3817 options.append("INITIALLY DEFERRED") 3818 elif self._match_text_seq("NORELY"): 3819 options.append("NORELY") 3820 elif self._match_text_seq("MATCH", "FULL"): 3821 options.append("MATCH FULL") 3822 else: 3823 break 3824 3825 return options 3826 3827 def _parse_references(self, match: bool = True) -> t.Optional[exp.Reference]: 3828 if match and not self._match(TokenType.REFERENCES): 3829 return None 3830 3831 expressions = None 3832 this = self._parse_table(schema=True) 3833 options = self._parse_key_constraint_options() 3834 return self.expression(exp.Reference, this=this, expressions=expressions, options=options) 3835 3836 def _parse_foreign_key(self) -> exp.ForeignKey: 3837 expressions = self._parse_wrapped_id_vars() 3838 reference = self._parse_references() 3839 options = {} 3840 3841 while self._match(TokenType.ON): 3842 if not self._match_set((TokenType.DELETE, TokenType.UPDATE)): 3843 self.raise_error("Expected DELETE or UPDATE") 3844 3845 kind = self._prev.text.lower() 3846 3847 if self._match_text_seq("NO", "ACTION"): 3848 action = "NO ACTION" 3849 elif self._match(TokenType.SET): 3850 self._match_set((TokenType.NULL, TokenType.DEFAULT)) 3851 action = "SET " + self._prev.text.upper() 3852 else: 3853 self._advance() 3854 action = self._prev.text.upper() 3855 3856 options[kind] = action 3857 3858 return self.expression( 3859 exp.ForeignKey, expressions=expressions, reference=reference, **options # type: ignore 3860 ) 3861 3862 def _parse_primary_key( 3863 self, wrapped_optional: bool = False, in_props: bool = False 3864 ) -> exp.PrimaryKeyColumnConstraint | exp.PrimaryKey: 3865 desc = ( 3866 self._match_set((TokenType.ASC, TokenType.DESC)) 3867 and self._prev.token_type == TokenType.DESC 3868 ) 3869 3870 if not in_props and not self._match(TokenType.L_PAREN, advance=False): 3871 return self.expression(exp.PrimaryKeyColumnConstraint, desc=desc) 3872 3873 expressions = self._parse_wrapped_csv(self._parse_field, optional=wrapped_optional) 3874 options = self._parse_key_constraint_options() 3875 return self.expression(exp.PrimaryKey, expressions=expressions, options=options) 3876 3877 def _parse_bracket(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3878 if not self._match_set((TokenType.L_BRACKET, TokenType.L_BRACE)): 3879 return this 3880 3881 bracket_kind = self._prev.token_type 3882 3883 if self._match(TokenType.COLON): 3884 expressions: t.List[exp.Expression] = [ 3885 self.expression(exp.Slice, expression=self._parse_conjunction()) 3886 ] 3887 else: 3888 expressions = self._parse_csv( 3889 lambda: self._parse_slice( 3890 self._parse_alias(self._parse_conjunction(), explicit=True) 3891 ) 3892 ) 3893 3894 # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs 3895 if bracket_kind == TokenType.L_BRACE: 3896 this = self.expression(exp.Struct, expressions=expressions) 3897 elif not this or this.name.upper() == "ARRAY": 3898 this = self.expression(exp.Array, expressions=expressions) 3899 else: 3900 expressions = apply_index_offset(this, expressions, -self.INDEX_OFFSET) 3901 this = self.expression(exp.Bracket, this=this, expressions=expressions) 3902 3903 if not self._match(TokenType.R_BRACKET) and bracket_kind == TokenType.L_BRACKET: 3904 self.raise_error("Expected ]") 3905 elif not self._match(TokenType.R_BRACE) and bracket_kind == TokenType.L_BRACE: 3906 self.raise_error("Expected }") 3907 3908 self._add_comments(this) 3909 return self._parse_bracket(this) 3910 3911 def _parse_slice(self, this: t.Optional[exp.Expression]) -> t.Optional[exp.Expression]: 3912 if self._match(TokenType.COLON): 3913 return self.expression(exp.Slice, this=this, expression=self._parse_conjunction()) 3914 return this 3915 3916 def _parse_case(self) -> t.Optional[exp.Expression]: 3917 ifs = [] 3918 default = None 3919 3920 comments = self._prev_comments 3921 expression = self._parse_conjunction() 3922 3923 while self._match(TokenType.WHEN): 3924 this = self._parse_conjunction() 3925 self._match(TokenType.THEN) 3926 then = self._parse_conjunction() 3927 ifs.append(self.expression(exp.If, this=this, true=then)) 3928 3929 if self._match(TokenType.ELSE): 3930 default = self._parse_conjunction() 3931 3932 if not self._match(TokenType.END): 3933 self.raise_error("Expected END after CASE", self._prev) 3934 3935 return self._parse_window( 3936 self.expression(exp.Case, comments=comments, this=expression, ifs=ifs, default=default) 3937 ) 3938 3939 def _parse_if(self) -> t.Optional[exp.Expression]: 3940 if self._match(TokenType.L_PAREN): 3941 args = self._parse_csv(self._parse_conjunction) 3942 this = self.validate_expression(exp.If.from_arg_list(args), args) 3943 self._match_r_paren() 3944 else: 3945 index = self._index - 1 3946 condition = self._parse_conjunction() 3947 3948 if not condition: 3949 self._retreat(index) 3950 return None 3951 3952 self._match(TokenType.THEN) 3953 true = self._parse_conjunction() 3954 false = self._parse_conjunction() if self._match(TokenType.ELSE) else None 3955 self._match(TokenType.END) 3956 this = self.expression(exp.If, this=condition, true=true, false=false) 3957 3958 return self._parse_window(this) 3959 3960 def _parse_next_value_for(self) -> t.Optional[exp.Expression]: 3961 if not self._match_text_seq("VALUE", "FOR"): 3962 self._retreat(self._index - 1) 3963 return None 3964 3965 return self.expression( 3966 exp.NextValueFor, 3967 this=self._parse_column(), 3968 order=self._match(TokenType.OVER) and self._parse_wrapped(self._parse_order), 3969 ) 3970 3971 def _parse_extract(self) -> exp.Extract: 3972 this = self._parse_function() or self._parse_var() or self._parse_type() 3973 3974 if self._match(TokenType.FROM): 3975 return self.expression(exp.Extract, this=this, expression=self._parse_bitwise()) 3976 3977 if not self._match(TokenType.COMMA): 3978 self.raise_error("Expected FROM or comma after EXTRACT", self._prev) 3979 3980 return self.expression(exp.Extract, this=this, expression=self._parse_bitwise()) 3981 3982 def _parse_any_value(self) -> exp.AnyValue: 3983 this = self._parse_lambda() 3984 is_max = None 3985 having = None 3986 3987 if self._match(TokenType.HAVING): 3988 self._match_texts(("MAX", "MIN")) 3989 is_max = self._prev.text == "MAX" 3990 having = self._parse_column() 3991 3992 return self.expression(exp.AnyValue, this=this, having=having, max=is_max) 3993 3994 def _parse_cast(self, strict: bool) -> exp.Expression: 3995 this = self._parse_conjunction() 3996 3997 if not self._match(TokenType.ALIAS): 3998 if self._match(TokenType.COMMA): 3999 return self.expression(exp.CastToStrType, this=this, to=self._parse_string()) 4000 4001 self.raise_error("Expected AS after CAST") 4002 4003 fmt = None 4004 to = self._parse_types() 4005 4006 if not to: 4007 self.raise_error("Expected TYPE after CAST") 4008 elif isinstance(to, exp.Identifier): 4009 to = exp.DataType.build(to.name, udt=True) 4010 elif to.this == exp.DataType.Type.CHAR: 4011 if self._match(TokenType.CHARACTER_SET): 4012 to = self.expression(exp.CharacterSet, this=self._parse_var_or_string()) 4013 elif self._match(TokenType.FORMAT): 4014 fmt_string = self._parse_string() 4015 fmt = self._parse_at_time_zone(fmt_string) 4016 4017 if to.this in exp.DataType.TEMPORAL_TYPES: 4018 this = self.expression( 4019 exp.StrToDate if to.this == exp.DataType.Type.DATE else exp.StrToTime, 4020 this=this, 4021 format=exp.Literal.string( 4022 format_time( 4023 fmt_string.this if fmt_string else "", 4024 self.FORMAT_MAPPING or self.TIME_MAPPING, 4025 self.FORMAT_TRIE or self.TIME_TRIE, 4026 ) 4027 ), 4028 ) 4029 4030 if isinstance(fmt, exp.AtTimeZone) and isinstance(this, exp.StrToTime): 4031 this.set("zone", fmt.args["zone"]) 4032 4033 return this 4034 4035 return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to, format=fmt) 4036 4037 def _parse_concat(self) -> t.Optional[exp.Expression]: 4038 args = self._parse_csv(self._parse_conjunction) 4039 if self.CONCAT_NULL_OUTPUTS_STRING: 4040 args = [ 4041 exp.func("COALESCE", exp.cast(arg, "text"), exp.Literal.string("")) 4042 for arg in args 4043 if arg 4044 ] 4045 4046 # Some dialects (e.g. Trino) don't allow a single-argument CONCAT call, so when 4047 # we find such a call we replace it with its argument. 4048 if len(args) == 1: 4049 return args[0] 4050 4051 return self.expression( 4052 exp.Concat if self.STRICT_STRING_CONCAT else exp.SafeConcat, expressions=args 4053 ) 4054 4055 def _parse_string_agg(self) -> exp.Expression: 4056 if self._match(TokenType.DISTINCT): 4057 args: t.List[t.Optional[exp.Expression]] = [ 4058 self.expression(exp.Distinct, expressions=[self._parse_conjunction()]) 4059 ] 4060 if self._match(TokenType.COMMA): 4061 args.extend(self._parse_csv(self._parse_conjunction)) 4062 else: 4063 args = self._parse_csv(self._parse_conjunction) # type: ignore 4064 4065 index = self._index 4066 if not self._match(TokenType.R_PAREN) and args: 4067 # postgres: STRING_AGG([DISTINCT] expression, separator [ORDER BY expression1 {ASC | DESC} [, ...]]) 4068 # bigquery: STRING_AGG([DISTINCT] expression [, separator] [ORDER BY key [{ASC | DESC}] [, ... ]] [LIMIT n]) 4069 args[-1] = self._parse_limit(this=self._parse_order(this=args[-1])) 4070 return self.expression(exp.GroupConcat, this=args[0], separator=seq_get(args, 1)) 4071 4072 # Checks if we can parse an order clause: WITHIN GROUP (ORDER BY <order_by_expression_list> [ASC | DESC]). 4073 # This is done "manually", instead of letting _parse_window parse it into an exp.WithinGroup node, so that 4074 # the STRING_AGG call is parsed like in MySQL / SQLite and can thus be transpiled more easily to them. 4075 if not self._match_text_seq("WITHIN", "GROUP"): 4076 self._retreat(index) 4077 return self.validate_expression(exp.GroupConcat.from_arg_list(args), args) 4078 4079 self._match_l_paren() # The corresponding match_r_paren will be called in parse_function (caller) 4080 order = self._parse_order(this=seq_get(args, 0)) 4081 return self.expression(exp.GroupConcat, this=order, separator=seq_get(args, 1)) 4082 4083 def _parse_convert(self, strict: bool) -> t.Optional[exp.Expression]: 4084 this = self._parse_bitwise() 4085 4086 if self._match(TokenType.USING): 4087 to: t.Optional[exp.Expression] = self.expression( 4088 exp.CharacterSet, this=self._parse_var() 4089 ) 4090 elif self._match(TokenType.COMMA): 4091 to = self._parse_types() 4092 else: 4093 to = None 4094 4095 return self.expression(exp.Cast if strict else exp.TryCast, this=this, to=to) 4096 4097 def _parse_decode(self) -> t.Optional[exp.Decode | exp.Case]: 4098 """ 4099 There are generally two variants of the DECODE function: 4100 4101 - DECODE(bin, charset) 4102 - DECODE(expression, search, result [, search, result] ... [, default]) 4103 4104 The second variant will always be parsed into a CASE expression. Note that NULL 4105 needs special treatment, since we need to explicitly check for it with `IS NULL`, 4106 instead of relying on pattern matching. 4107 """ 4108 args = self._parse_csv(self._parse_conjunction) 4109 4110 if len(args) < 3: 4111 return self.expression(exp.Decode, this=seq_get(args, 0), charset=seq_get(args, 1)) 4112 4113 expression, *expressions = args 4114 if not expression: 4115 return None 4116 4117 ifs = [] 4118 for search, result in zip(expressions[::2], expressions[1::2]): 4119 if not search or not result: 4120 return None 4121 4122 if isinstance(search, exp.Literal): 4123 ifs.append( 4124 exp.If(this=exp.EQ(this=expression.copy(), expression=search), true=result) 4125 ) 4126 elif isinstance(search, exp.Null): 4127 ifs.append( 4128 exp.If(this=exp.Is(this=expression.copy(), expression=exp.Null()), true=result) 4129 ) 4130 else: 4131 cond = exp.or_( 4132 exp.EQ(this=expression.copy(), expression=search), 4133 exp.and_( 4134 exp.Is(this=expression.copy(), expression=exp.Null()), 4135 exp.Is(this=search.copy(), expression=exp.Null()), 4136 copy=False, 4137 ), 4138 copy=False, 4139 ) 4140 ifs.append(exp.If(this=cond, true=result)) 4141 4142 return exp.Case(ifs=ifs, default=expressions[-1] if len(expressions) % 2 == 1 else None) 4143 4144 def _parse_json_key_value(self) -> t.Optional[exp.JSONKeyValue]: 4145 self._match_text_seq("KEY") 4146 key = self._parse_field() 4147 self._match_set((TokenType.COLON, TokenType.COMMA)) 4148 self._match_text_seq("VALUE") 4149 value = self._parse_column() 4150 4151 if not key and not value: 4152 return None 4153 return self.expression(exp.JSONKeyValue, this=key, expression=value) 4154 4155 def _parse_json_object(self) -> exp.JSONObject: 4156 star = self._parse_star() 4157 expressions = [star] if star else self._parse_csv(self._parse_json_key_value) 4158 4159 null_handling = None 4160 if self._match_text_seq("NULL", "ON", "NULL"): 4161 null_handling = "NULL ON NULL" 4162 elif self._match_text_seq("ABSENT", "ON", "NULL"): 4163 null_handling = "ABSENT ON NULL" 4164 4165 unique_keys = None 4166 if self._match_text_seq("WITH", "UNIQUE"): 4167 unique_keys = True 4168 elif self._match_text_seq("WITHOUT", "UNIQUE"): 4169 unique_keys = False 4170 4171 self._match_text_seq("KEYS") 4172 4173 return_type = self._match_text_seq("RETURNING") and self._parse_type() 4174 format_json = self._match_text_seq("FORMAT", "JSON") 4175 encoding = self._match_text_seq("ENCODING") and self._parse_var() 4176 4177 return self.expression( 4178 exp.JSONObject, 4179 expressions=expressions, 4180 null_handling=null_handling, 4181 unique_keys=unique_keys, 4182 return_type=return_type, 4183 format_json=format_json, 4184 encoding=encoding, 4185 ) 4186 4187 def _parse_logarithm(self) -> exp.Func: 4188 # Default argument order is base, expression 4189 args = self._parse_csv(self._parse_range) 4190 4191 if len(args) > 1: 4192 if not self.LOG_BASE_FIRST: 4193 args.reverse() 4194 return exp.Log.from_arg_list(args) 4195 4196 return self.expression( 4197 exp.Ln if self.LOG_DEFAULTS_TO_LN else exp.Log, this=seq_get(args, 0) 4198 ) 4199 4200 def _parse_match_against(self) -> exp.MatchAgainst: 4201 expressions = self._parse_csv(self._parse_column) 4202 4203 self._match_text_seq(")", "AGAINST", "(") 4204 4205 this = self._parse_string() 4206 4207 if self._match_text_seq("IN", "NATURAL", "LANGUAGE", "MODE"): 4208 modifier = "IN NATURAL LANGUAGE MODE" 4209 if self._match_text_seq("WITH", "QUERY", "EXPANSION"): 4210 modifier = f"{modifier} WITH QUERY EXPANSION" 4211 elif self._match_text_seq("IN", "BOOLEAN", "MODE"): 4212 modifier = "IN BOOLEAN MODE" 4213 elif self._match_text_seq("WITH", "QUERY", "EXPANSION"): 4214 modifier = "WITH QUERY EXPANSION" 4215 else: 4216 modifier = None 4217 4218 return self.expression( 4219 exp.MatchAgainst, this=this, expressions=expressions, modifier=modifier 4220 ) 4221 4222 # https://learn.microsoft.com/en-us/sql/t-sql/functions/openjson-transact-sql?view=sql-server-ver16 4223 def _parse_open_json(self) -> exp.OpenJSON: 4224 this = self._parse_bitwise() 4225 path = self._match(TokenType.COMMA) and self._parse_string() 4226 4227 def _parse_open_json_column_def() -> exp.OpenJSONColumnDef: 4228 this = self._parse_field(any_token=True) 4229 kind = self._parse_types() 4230 path = self._parse_string() 4231 as_json = self._match_pair(TokenType.ALIAS, TokenType.JSON) 4232 4233 return self.expression( 4234 exp.OpenJSONColumnDef, this=this, kind=kind, path=path, as_json=as_json 4235 ) 4236 4237 expressions = None 4238 if self._match_pair(TokenType.R_PAREN, TokenType.WITH): 4239 self._match_l_paren() 4240 expressions = self._parse_csv(_parse_open_json_column_def) 4241 4242 return self.expression(exp.OpenJSON, this=this, path=path, expressions=expressions) 4243 4244 def _parse_position(self, haystack_first: bool = False) -> exp.StrPosition: 4245 args = self._parse_csv(self._parse_bitwise) 4246 4247 if self._match(TokenType.IN): 4248 return self.expression( 4249 exp.StrPosition, this=self._parse_bitwise(), substr=seq_get(args, 0) 4250 ) 4251 4252 if haystack_first: 4253 haystack = seq_get(args, 0) 4254 needle = seq_get(args, 1) 4255 else: 4256 needle = seq_get(args, 0) 4257 haystack = seq_get(args, 1) 4258 4259 return self.expression( 4260 exp.StrPosition, this=haystack, substr=needle, position=seq_get(args, 2) 4261 ) 4262 4263 def _parse_join_hint(self, func_name: str) -> exp.JoinHint: 4264 args = self._parse_csv(self._parse_table) 4265 return exp.JoinHint(this=func_name.upper(), expressions=args) 4266 4267 def _parse_substring(self) -> exp.Substring: 4268 # Postgres supports the form: substring(string [from int] [for int]) 4269 # https://www.postgresql.org/docs/9.1/functions-string.html @ Table 9-6 4270 4271 args = t.cast(t.List[t.Optional[exp.Expression]], self._parse_csv(self._parse_bitwise)) 4272 4273 if self._match(TokenType.FROM): 4274 args.append(self._parse_bitwise()) 4275 if self._match(TokenType.FOR): 4276 args.append(self._parse_bitwise()) 4277 4278 return self.validate_expression(exp.Substring.from_arg_list(args), args) 4279 4280 def _parse_trim(self) -> exp.Trim: 4281 # https://www.w3resource.com/sql/character-functions/trim.php 4282 # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html 4283 4284 position = None 4285 collation = None 4286 4287 if self._match_texts(self.TRIM_TYPES): 4288 position = self._prev.text.upper() 4289 4290 expression = self._parse_bitwise() 4291 if self._match_set((TokenType.FROM, TokenType.COMMA)): 4292 this = self._parse_bitwise() 4293 else: 4294 this = expression 4295 expression = None 4296 4297 if self._match(TokenType.COLLATE): 4298 collation = self._parse_bitwise() 4299 4300 return self.expression( 4301 exp.Trim, this=this, position=position, expression=expression, collation=collation 4302 ) 4303 4304 def _parse_window_clause(self) -> t.Optional[t.List[exp.Expression]]: 4305 return self._match(TokenType.WINDOW) and self._parse_csv(self._parse_named_window) 4306 4307 def _parse_named_window(self) -> t.Optional[exp.Expression]: 4308 return self._parse_window(self._parse_id_var(), alias=True) 4309 4310 def _parse_respect_or_ignore_nulls( 4311 self, this: t.Optional[exp.Expression] 4312 ) -> t.Optional[exp.Expression]: 4313 if self._match_text_seq("IGNORE", "NULLS"): 4314 return self.expression(exp.IgnoreNulls, this=this) 4315 if self._match_text_seq("RESPECT", "NULLS"): 4316 return self.expression(exp.RespectNulls, this=this) 4317 return this 4318 4319 def _parse_window( 4320 self, this: t.Optional[exp.Expression], alias: bool = False 4321 ) -> t.Optional[exp.Expression]: 4322 if self._match_pair(TokenType.FILTER, TokenType.L_PAREN): 4323 self._match(TokenType.WHERE) 4324 this = self.expression( 4325 exp.Filter, this=this, expression=self._parse_where(skip_where_token=True) 4326 ) 4327 self._match_r_paren() 4328 4329 # T-SQL allows the OVER (...) syntax after WITHIN GROUP. 4330 # https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16 4331 if self._match_text_seq("WITHIN", "GROUP"): 4332 order = self._parse_wrapped(self._parse_order) 4333 this = self.expression(exp.WithinGroup, this=this, expression=order) 4334 4335 # SQL spec defines an optional [ { IGNORE | RESPECT } NULLS ] OVER 4336 # Some dialects choose to implement and some do not. 4337 # https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html 4338 4339 # There is some code above in _parse_lambda that handles 4340 # SELECT FIRST_VALUE(TABLE.COLUMN IGNORE|RESPECT NULLS) OVER ... 4341 4342 # The below changes handle 4343 # SELECT FIRST_VALUE(TABLE.COLUMN) IGNORE|RESPECT NULLS OVER ... 4344 4345 # Oracle allows both formats 4346 # (https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/img_text/first_value.html) 4347 # and Snowflake chose to do the same for familiarity 4348 # https://docs.snowflake.com/en/sql-reference/functions/first_value.html#usage-notes 4349 this = self._parse_respect_or_ignore_nulls(this) 4350 4351 # bigquery select from window x AS (partition by ...) 4352 if alias: 4353 over = None 4354 self._match(TokenType.ALIAS) 4355 elif not self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS): 4356 return this 4357 else: 4358 over = self._prev.text.upper() 4359 4360 if not self._match(TokenType.L_PAREN): 4361 return self.expression( 4362 exp.Window, this=this, alias=self._parse_id_var(False), over=over 4363 ) 4364 4365 window_alias = self._parse_id_var(any_token=False, tokens=self.WINDOW_ALIAS_TOKENS) 4366 4367 first = self._match(TokenType.FIRST) 4368 if self._match_text_seq("LAST"): 4369 first = False 4370 4371 partition, order = self._parse_partition_and_order() 4372 kind = self._match_set((TokenType.ROWS, TokenType.RANGE)) and self._prev.text 4373 4374 if kind: 4375 self._match(TokenType.BETWEEN) 4376 start = self._parse_window_spec() 4377 self._match(TokenType.AND) 4378 end = self._parse_window_spec() 4379 4380 spec = self.expression( 4381 exp.WindowSpec, 4382 kind=kind, 4383 start=start["value"], 4384 start_side=start["side"], 4385 end=end["value"], 4386 end_side=end["side"], 4387 ) 4388 else: 4389 spec = None 4390 4391 self._match_r_paren() 4392 4393 window = self.expression( 4394 exp.Window, 4395 this=this, 4396 partition_by=partition, 4397 order=order, 4398 spec=spec, 4399 alias=window_alias, 4400 over=over, 4401 first=first, 4402 ) 4403 4404 # This covers Oracle's FIRST/LAST syntax: aggregate KEEP (...) OVER (...) 4405 if self._match_set(self.WINDOW_BEFORE_PAREN_TOKENS, advance=False): 4406 return self._parse_window(window, alias=alias) 4407 4408 return window 4409 4410 def _parse_partition_and_order( 4411 self, 4412 ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]: 4413 return self._parse_partition_by(), self._parse_order() 4414 4415 def _parse_window_spec(self) -> t.Dict[str, t.Optional[str | exp.Expression]]: 4416 self._match(TokenType.BETWEEN) 4417 4418 return { 4419 "value": ( 4420 (self._match_text_seq("UNBOUNDED") and "UNBOUNDED") 4421 or (self._match_text_seq("CURRENT", "ROW") and "CURRENT ROW") 4422 or self._parse_bitwise() 4423 ), 4424 "side": self._match_texts(self.WINDOW_SIDES) and self._prev.text, 4425 } 4426 4427 def _parse_alias( 4428 self, this: t.Optional[exp.Expression], explicit: bool = False 4429 ) -> t.Optional[exp.Expression]: 4430 any_token = self._match(TokenType.ALIAS) 4431 4432 if explicit and not any_token: 4433 return this 4434 4435 if self._match(TokenType.L_PAREN): 4436 aliases = self.expression( 4437 exp.Aliases, 4438 this=this, 4439 expressions=self._parse_csv(lambda: self._parse_id_var(any_token)), 4440 ) 4441 self._match_r_paren(aliases) 4442 return aliases 4443 4444 alias = self._parse_id_var(any_token) 4445 4446 if alias: 4447 return self.expression(exp.Alias, this=this, alias=alias) 4448 4449 return this 4450 4451 def _parse_id_var( 4452 self, 4453 any_token: bool = True, 4454 tokens: t.Optional[t.Collection[TokenType]] = None, 4455 ) -> t.Optional[exp.Expression]: 4456 identifier = self._parse_identifier() 4457 4458 if identifier: 4459 return identifier 4460 4461 if (any_token and self._advance_any()) or self._match_set(tokens or self.ID_VAR_TOKENS): 4462 quoted = self._prev.token_type == TokenType.STRING 4463 return exp.Identifier(this=self._prev.text, quoted=quoted) 4464 4465 return None 4466 4467 def _parse_string(self) -> t.Optional[exp.Expression]: 4468 if self._match(TokenType.STRING): 4469 return self.PRIMARY_PARSERS[TokenType.STRING](self, self._prev) 4470 return self._parse_placeholder() 4471 4472 def _parse_string_as_identifier(self) -> t.Optional[exp.Identifier]: 4473 return exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True) 4474 4475 def _parse_number(self) -> t.Optional[exp.Expression]: 4476 if self._match(TokenType.NUMBER): 4477 return self.PRIMARY_PARSERS[TokenType.NUMBER](self, self._prev) 4478 return self._parse_placeholder() 4479 4480 def _parse_identifier(self) -> t.Optional[exp.Expression]: 4481 if self._match(TokenType.IDENTIFIER): 4482 return self.expression(exp.Identifier, this=self._prev.text, quoted=True) 4483 return self._parse_placeholder() 4484 4485 def _parse_var( 4486 self, any_token: bool = False, tokens: t.Optional[t.Collection[TokenType]] = None 4487 ) -> t.Optional[exp.Expression]: 4488 if ( 4489 (any_token and self._advance_any()) 4490 or self._match(TokenType.VAR) 4491 or (self._match_set(tokens) if tokens else False) 4492 ): 4493 return self.expression(exp.Var, this=self._prev.text) 4494 return self._parse_placeholder() 4495 4496 def _advance_any(self) -> t.Optional[Token]: 4497 if self._curr and self._curr.token_type not in self.RESERVED_KEYWORDS: 4498 self._advance() 4499 return self._prev 4500 return None 4501 4502 def _parse_var_or_string(self) -> t.Optional[exp.Expression]: 4503 return self._parse_var() or self._parse_string() 4504 4505 def _parse_null(self) -> t.Optional[exp.Expression]: 4506 if self._match(TokenType.NULL): 4507 return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev) 4508 return self._parse_placeholder() 4509 4510 def _parse_boolean(self) -> t.Optional[exp.Expression]: 4511 if self._match(TokenType.TRUE): 4512 return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev) 4513 if self._match(TokenType.FALSE): 4514 return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev) 4515 return self._parse_placeholder() 4516 4517 def _parse_star(self) -> t.Optional[exp.Expression]: 4518 if self._match(TokenType.STAR): 4519 return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev) 4520 return self._parse_placeholder() 4521 4522 def _parse_parameter(self) -> exp.Parameter: 4523 wrapped = self._match(TokenType.L_BRACE) 4524 this = self._parse_var() or self._parse_identifier() or self._parse_primary() 4525 self._match(TokenType.R_BRACE) 4526 return self.expression(exp.Parameter, this=this, wrapped=wrapped) 4527 4528 def _parse_placeholder(self) -> t.Optional[exp.Expression]: 4529 if self._match_set(self.PLACEHOLDER_PARSERS): 4530 placeholder = self.PLACEHOLDER_PARSERS[self._prev.token_type](self) 4531 if placeholder: 4532 return placeholder 4533 self._advance(-1) 4534 return None 4535 4536 def _parse_except(self) -> t.Optional[t.List[exp.Expression]]: 4537 if not self._match(TokenType.EXCEPT): 4538 return None 4539 if self._match(TokenType.L_PAREN, advance=False): 4540 return self._parse_wrapped_csv(self._parse_column) 4541 return self._parse_csv(self._parse_column) 4542 4543 def _parse_replace(self) -> t.Optional[t.List[exp.Expression]]: 4544 if not self._match(TokenType.REPLACE): 4545 return None 4546 if self._match(TokenType.L_PAREN, advance=False): 4547 return self._parse_wrapped_csv(self._parse_expression) 4548 return self._parse_expressions() 4549 4550 def _parse_csv( 4551 self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA 4552 ) -> t.List[exp.Expression]: 4553 parse_result = parse_method() 4554 items = [parse_result] if parse_result is not None else [] 4555 4556 while self._match(sep): 4557 self._add_comments(parse_result) 4558 parse_result = parse_method() 4559 if parse_result is not None: 4560 items.append(parse_result) 4561 4562 return items 4563 4564 def _parse_tokens( 4565 self, parse_method: t.Callable, expressions: t.Dict 4566 ) -> t.Optional[exp.Expression]: 4567 this = parse_method() 4568 4569 while self._match_set(expressions): 4570 this = self.expression( 4571 expressions[self._prev.token_type], 4572 this=this, 4573 comments=self._prev_comments, 4574 expression=parse_method(), 4575 ) 4576 4577 return this 4578 4579 def _parse_wrapped_id_vars(self, optional: bool = False) -> t.List[exp.Expression]: 4580 return self._parse_wrapped_csv(self._parse_id_var, optional=optional) 4581 4582 def _parse_wrapped_csv( 4583 self, parse_method: t.Callable, sep: TokenType = TokenType.COMMA, optional: bool = False 4584 ) -> t.List[exp.Expression]: 4585 return self._parse_wrapped( 4586 lambda: self._parse_csv(parse_method, sep=sep), optional=optional 4587 ) 4588 4589 def _parse_wrapped(self, parse_method: t.Callable, optional: bool = False) -> t.Any: 4590 wrapped = self._match(TokenType.L_PAREN) 4591 if not wrapped and not optional: 4592 self.raise_error("Expecting (") 4593 parse_result = parse_method() 4594 if wrapped: 4595 self._match_r_paren() 4596 return parse_result 4597 4598 def _parse_expressions(self) -> t.List[exp.Expression]: 4599 return self._parse_csv(self._parse_expression) 4600 4601 def _parse_select_or_expression(self, alias: bool = False) -> t.Optional[exp.Expression]: 4602 return self._parse_select() or self._parse_set_operations( 4603 self._parse_expression() if alias else self._parse_conjunction() 4604 ) 4605 4606 def _parse_ddl_select(self) -> t.Optional[exp.Expression]: 4607 return self._parse_query_modifiers( 4608 self._parse_set_operations(self._parse_select(nested=True, parse_subquery_alias=False)) 4609 ) 4610 4611 def _parse_transaction(self) -> exp.Transaction | exp.Command: 4612 this = None 4613 if self._match_texts(self.TRANSACTION_KIND): 4614 this = self._prev.text 4615 4616 self._match_texts({"TRANSACTION", "WORK"}) 4617 4618 modes = [] 4619 while True: 4620 mode = [] 4621 while self._match(TokenType.VAR): 4622 mode.append(self._prev.text) 4623 4624 if mode: 4625 modes.append(" ".join(mode)) 4626 if not self._match(TokenType.COMMA): 4627 break 4628 4629 return self.expression(exp.Transaction, this=this, modes=modes) 4630 4631 def _parse_commit_or_rollback(self) -> exp.Commit | exp.Rollback: 4632 chain = None 4633 savepoint = None 4634 is_rollback = self._prev.token_type == TokenType.ROLLBACK 4635 4636 self._match_texts({"TRANSACTION", "WORK"}) 4637 4638 if self._match_text_seq("TO"): 4639 self._match_text_seq("SAVEPOINT") 4640 savepoint = self._parse_id_var() 4641 4642 if self._match(TokenType.AND): 4643 chain = not self._match_text_seq("NO") 4644 self._match_text_seq("CHAIN") 4645 4646 if is_rollback: 4647 return self.expression(exp.Rollback, savepoint=savepoint) 4648 4649 return self.expression(exp.Commit, chain=chain) 4650 4651 def _parse_add_column(self) -> t.Optional[exp.Expression]: 4652 if not self._match_text_seq("ADD"): 4653 return None 4654 4655 self._match(TokenType.COLUMN) 4656 exists_column = self._parse_exists(not_=True) 4657 expression = self._parse_field_def() 4658 4659 if expression: 4660 expression.set("exists", exists_column) 4661 4662 # https://docs.databricks.com/delta/update-schema.html#explicitly-update-schema-to-add-columns 4663 if self._match_texts(("FIRST", "AFTER")): 4664 position = self._prev.text 4665 column_position = self.expression( 4666 exp.ColumnPosition, this=self._parse_column(), position=position 4667 ) 4668 expression.set("position", column_position) 4669 4670 return expression 4671 4672 def _parse_drop_column(self) -> t.Optional[exp.Drop | exp.Command]: 4673 drop = self._match(TokenType.DROP) and self._parse_drop() 4674 if drop and not isinstance(drop, exp.Command): 4675 drop.set("kind", drop.args.get("kind", "COLUMN")) 4676 return drop 4677 4678 # https://docs.aws.amazon.com/athena/latest/ug/alter-table-drop-partition.html 4679 def _parse_drop_partition(self, exists: t.Optional[bool] = None) -> exp.DropPartition: 4680 return self.expression( 4681 exp.DropPartition, expressions=self._parse_csv(self._parse_partition), exists=exists 4682 ) 4683 4684 def _parse_add_constraint(self) -> exp.AddConstraint: 4685 this = None 4686 kind = self._prev.token_type 4687 4688 if kind == TokenType.CONSTRAINT: 4689 this = self._parse_id_var() 4690 4691 if self._match_text_seq("CHECK"): 4692 expression = self._parse_wrapped(self._parse_conjunction) 4693 enforced = self._match_text_seq("ENFORCED") 4694 4695 return self.expression( 4696 exp.AddConstraint, this=this, expression=expression, enforced=enforced 4697 ) 4698 4699 if kind == TokenType.FOREIGN_KEY or self._match(TokenType.FOREIGN_KEY): 4700 expression = self._parse_foreign_key() 4701 elif kind == TokenType.PRIMARY_KEY or self._match(TokenType.PRIMARY_KEY): 4702 expression = self._parse_primary_key() 4703 else: 4704 expression = None 4705 4706 return self.expression(exp.AddConstraint, this=this, expression=expression) 4707 4708 def _parse_alter_table_add(self) -> t.List[exp.Expression]: 4709 index = self._index - 1 4710 4711 if self._match_set(self.ADD_CONSTRAINT_TOKENS): 4712 return self._parse_csv(self._parse_add_constraint) 4713 4714 self._retreat(index) 4715 if not self.ALTER_TABLE_ADD_COLUMN_KEYWORD and self._match_text_seq("ADD"): 4716 return self._parse_csv(self._parse_field_def) 4717 4718 return self._parse_csv(self._parse_add_column) 4719 4720 def _parse_alter_table_alter(self) -> exp.AlterColumn: 4721 self._match(TokenType.COLUMN) 4722 column = self._parse_field(any_token=True) 4723 4724 if self._match_pair(TokenType.DROP, TokenType.DEFAULT): 4725 return self.expression(exp.AlterColumn, this=column, drop=True) 4726 if self._match_pair(TokenType.SET, TokenType.DEFAULT): 4727 return self.expression(exp.AlterColumn, this=column, default=self._parse_conjunction()) 4728 4729 self._match_text_seq("SET", "DATA") 4730 return self.expression( 4731 exp.AlterColumn, 4732 this=column, 4733 dtype=self._match_text_seq("TYPE") and self._parse_types(), 4734 collate=self._match(TokenType.COLLATE) and self._parse_term(), 4735 using=self._match(TokenType.USING) and self._parse_conjunction(), 4736 ) 4737 4738 def _parse_alter_table_drop(self) -> t.List[exp.Expression]: 4739 index = self._index - 1 4740 4741 partition_exists = self._parse_exists() 4742 if self._match(TokenType.PARTITION, advance=False): 4743 return self._parse_csv(lambda: self._parse_drop_partition(exists=partition_exists)) 4744 4745 self._retreat(index) 4746 return self._parse_csv(self._parse_drop_column) 4747 4748 def _parse_alter_table_rename(self) -> exp.RenameTable: 4749 self._match_text_seq("TO") 4750 return self.expression(exp.RenameTable, this=self._parse_table(schema=True)) 4751 4752 def _parse_alter(self) -> exp.AlterTable | exp.Command: 4753 start = self._prev 4754 4755 if not self._match(TokenType.TABLE): 4756 return self._parse_as_command(start) 4757 4758 exists = self._parse_exists() 4759 this = self._parse_table(schema=True) 4760 4761 if self._next: 4762 self._advance() 4763 4764 parser = self.ALTER_PARSERS.get(self._prev.text.upper()) if self._prev else None 4765 if parser: 4766 actions = ensure_list(parser(self)) 4767 4768 if not self._curr: 4769 return self.expression( 4770 exp.AlterTable, 4771 this=this, 4772 exists=exists, 4773 actions=actions, 4774 ) 4775 return self._parse_as_command(start) 4776 4777 def _parse_merge(self) -> exp.Merge: 4778 self._match(TokenType.INTO) 4779 target = self._parse_table() 4780 4781 if target and self._match(TokenType.ALIAS, advance=False): 4782 target.set("alias", self._parse_table_alias()) 4783 4784 self._match(TokenType.USING) 4785 using = self._parse_table() 4786 4787 self._match(TokenType.ON) 4788 on = self._parse_conjunction() 4789 4790 whens = [] 4791 while self._match(TokenType.WHEN): 4792 matched = not self._match(TokenType.NOT) 4793 self._match_text_seq("MATCHED") 4794 source = ( 4795 False 4796 if self._match_text_seq("BY", "TARGET") 4797 else self._match_text_seq("BY", "SOURCE") 4798 ) 4799 condition = self._parse_conjunction() if self._match(TokenType.AND) else None 4800 4801 self._match(TokenType.THEN) 4802 4803 if self._match(TokenType.INSERT): 4804 _this = self._parse_star() 4805 if _this: 4806 then: t.Optional[exp.Expression] = self.expression(exp.Insert, this=_this) 4807 else: 4808 then = self.expression( 4809 exp.Insert, 4810 this=self._parse_value(), 4811 expression=self._match(TokenType.VALUES) and self._parse_value(), 4812 ) 4813 elif self._match(TokenType.UPDATE): 4814 expressions = self._parse_star() 4815 if expressions: 4816 then = self.expression(exp.Update, expressions=expressions) 4817 else: 4818 then = self.expression( 4819 exp.Update, 4820 expressions=self._match(TokenType.SET) 4821 and self._parse_csv(self._parse_equality), 4822 ) 4823 elif self._match(TokenType.DELETE): 4824 then = self.expression(exp.Var, this=self._prev.text) 4825 else: 4826 then = None 4827 4828 whens.append( 4829 self.expression( 4830 exp.When, 4831 matched=matched, 4832 source=source, 4833 condition=condition, 4834 then=then, 4835 ) 4836 ) 4837 4838 return self.expression( 4839 exp.Merge, 4840 this=target, 4841 using=using, 4842 on=on, 4843 expressions=whens, 4844 ) 4845 4846 def _parse_show(self) -> t.Optional[exp.Expression]: 4847 parser = self._find_parser(self.SHOW_PARSERS, self.SHOW_TRIE) 4848 if parser: 4849 return parser(self) 4850 self._advance() 4851 return self.expression(exp.Show, this=self._prev.text.upper()) 4852 4853 def _parse_set_item_assignment( 4854 self, kind: t.Optional[str] = None 4855 ) -> t.Optional[exp.Expression]: 4856 index = self._index 4857 4858 if kind in {"GLOBAL", "SESSION"} and self._match_text_seq("TRANSACTION"): 4859 return self._parse_set_transaction(global_=kind == "GLOBAL") 4860 4861 left = self._parse_primary() or self._parse_id_var() 4862 4863 if not self._match_texts(("=", "TO")): 4864 self._retreat(index) 4865 return None 4866 4867 right = self._parse_statement() or self._parse_id_var() 4868 this = self.expression(exp.EQ, this=left, expression=right) 4869 4870 return self.expression(exp.SetItem, this=this, kind=kind) 4871 4872 def _parse_set_transaction(self, global_: bool = False) -> exp.Expression: 4873 self._match_text_seq("TRANSACTION") 4874 characteristics = self._parse_csv( 4875 lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS) 4876 ) 4877 return self.expression( 4878 exp.SetItem, 4879 expressions=characteristics, 4880 kind="TRANSACTION", 4881 **{"global": global_}, # type: ignore 4882 ) 4883 4884 def _parse_set_item(self) -> t.Optional[exp.Expression]: 4885 parser = self._find_parser(self.SET_PARSERS, self.SET_TRIE) 4886 return parser(self) if parser else self._parse_set_item_assignment(kind=None) 4887 4888 def _parse_set(self, unset: bool = False, tag: bool = False) -> exp.Set | exp.Command: 4889 index = self._index 4890 set_ = self.expression( 4891 exp.Set, expressions=self._parse_csv(self._parse_set_item), unset=unset, tag=tag 4892 ) 4893 4894 if self._curr: 4895 self._retreat(index) 4896 return self._parse_as_command(self._prev) 4897 4898 return set_ 4899 4900 def _parse_var_from_options(self, options: t.Collection[str]) -> t.Optional[exp.Var]: 4901 for option in options: 4902 if self._match_text_seq(*option.split(" ")): 4903 return exp.var(option) 4904 return None 4905 4906 def _parse_as_command(self, start: Token) -> exp.Command: 4907 while self._curr: 4908 self._advance() 4909 text = self._find_sql(start, self._prev) 4910 size = len(start.text) 4911 return exp.Command(this=text[:size], expression=text[size:]) 4912 4913 def _parse_dict_property(self, this: str) -> exp.DictProperty: 4914 settings = [] 4915 4916 self._match_l_paren() 4917 kind = self._parse_id_var() 4918 4919 if self._match(TokenType.L_PAREN): 4920 while True: 4921 key = self._parse_id_var() 4922 value = self._parse_primary() 4923 4924 if not key and value is None: 4925 break 4926 settings.append(self.expression(exp.DictSubProperty, this=key, value=value)) 4927 self._match(TokenType.R_PAREN) 4928 4929 self._match_r_paren() 4930 4931 return self.expression( 4932 exp.DictProperty, 4933 this=this, 4934 kind=kind.this if kind else None, 4935 settings=settings, 4936 ) 4937 4938 def _parse_dict_range(self, this: str) -> exp.DictRange: 4939 self._match_l_paren() 4940 has_min = self._match_text_seq("MIN") 4941 if has_min: 4942 min = self._parse_var() or self._parse_primary() 4943 self._match_text_seq("MAX") 4944 max = self._parse_var() or self._parse_primary() 4945 else: 4946 max = self._parse_var() or self._parse_primary() 4947 min = exp.Literal.number(0) 4948 self._match_r_paren() 4949 return self.expression(exp.DictRange, this=this, min=min, max=max) 4950 4951 def _parse_comprehension(self, this: exp.Expression) -> exp.Comprehension: 4952 expression = self._parse_column() 4953 self._match(TokenType.IN) 4954 iterator = self._parse_column() 4955 condition = self._parse_conjunction() if self._match_text_seq("IF") else None 4956 return self.expression( 4957 exp.Comprehension, 4958 this=this, 4959 expression=expression, 4960 iterator=iterator, 4961 condition=condition, 4962 ) 4963 4964 def _find_parser( 4965 self, parsers: t.Dict[str, t.Callable], trie: t.Dict 4966 ) -> t.Optional[t.Callable]: 4967 if not self._curr: 4968 return None 4969 4970 index = self._index 4971 this = [] 4972 while True: 4973 # The current token might be multiple words 4974 curr = self._curr.text.upper() 4975 key = curr.split(" ") 4976 this.append(curr) 4977 4978 self._advance() 4979 result, trie = in_trie(trie, key) 4980 if result == TrieResult.FAILED: 4981 break 4982 4983 if result == TrieResult.EXISTS: 4984 subparser = parsers[" ".join(this)] 4985 return subparser 4986 4987 self._retreat(index) 4988 return None 4989 4990 def _match(self, token_type, advance=True, expression=None): 4991 if not self._curr: 4992 return None 4993 4994 if self._curr.token_type == token_type: 4995 if advance: 4996 self._advance() 4997 self._add_comments(expression) 4998 return True 4999 5000 return None 5001 5002 def _match_set(self, types, advance=True): 5003 if not self._curr: 5004 return None 5005 5006 if self._curr.token_type in types: 5007 if advance: 5008 self._advance() 5009 return True 5010 5011 return None 5012 5013 def _match_pair(self, token_type_a, token_type_b, advance=True): 5014 if not self._curr or not self._next: 5015 return None 5016 5017 if self._curr.token_type == token_type_a and self._next.token_type == token_type_b: 5018 if advance: 5019 self._advance(2) 5020 return True 5021 5022 return None 5023 5024 def _match_l_paren(self, expression: t.Optional[exp.Expression] = None) -> None: 5025 if not self._match(TokenType.L_PAREN, expression=expression): 5026 self.raise_error("Expecting (") 5027 5028 def _match_r_paren(self, expression: t.Optional[exp.Expression] = None) -> None: 5029 if not self._match(TokenType.R_PAREN, expression=expression): 5030 self.raise_error("Expecting )") 5031 5032 def _match_texts(self, texts, advance=True): 5033 if self._curr and self._curr.text.upper() in texts: 5034 if advance: 5035 self._advance() 5036 return True 5037 return False 5038 5039 def _match_text_seq(self, *texts, advance=True): 5040 index = self._index 5041 for text in texts: 5042 if self._curr and self._curr.text.upper() == text: 5043 self._advance() 5044 else: 5045 self._retreat(index) 5046 return False 5047 5048 if not advance: 5049 self._retreat(index) 5050 5051 return True 5052 5053 @t.overload 5054 def _replace_columns_with_dots(self, this: exp.Expression) -> exp.Expression: 5055 ... 5056 5057 @t.overload 5058 def _replace_columns_with_dots( 5059 self, this: t.Optional[exp.Expression] 5060 ) -> t.Optional[exp.Expression]: 5061 ... 5062 5063 def _replace_columns_with_dots(self, this): 5064 if isinstance(this, exp.Dot): 5065 exp.replace_children(this, self._replace_columns_with_dots) 5066 elif isinstance(this, exp.Column): 5067 exp.replace_children(this, self._replace_columns_with_dots) 5068 table = this.args.get("table") 5069 this = ( 5070 self.expression(exp.Dot, this=table, expression=this.this) if table else this.this 5071 ) 5072 5073 return this 5074 5075 def _replace_lambda( 5076 self, node: t.Optional[exp.Expression], lambda_variables: t.Set[str] 5077 ) -> t.Optional[exp.Expression]: 5078 if not node: 5079 return node 5080 5081 for column in node.find_all(exp.Column): 5082 if column.parts[0].name in lambda_variables: 5083 dot_or_id = column.to_dot() if column.table else column.this 5084 parent = column.parent 5085 5086 while isinstance(parent, exp.Dot): 5087 if not isinstance(parent.parent, exp.Dot): 5088 parent.replace(dot_or_id) 5089 break 5090 parent = parent.parent 5091 else: 5092 if column is node: 5093 node = dot_or_id 5094 else: 5095 column.replace(dot_or_id) 5096 return node
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: Determines 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
889 def __init__( 890 self, 891 error_level: t.Optional[ErrorLevel] = None, 892 error_message_context: int = 100, 893 max_errors: int = 3, 894 ): 895 self.error_level = error_level or ErrorLevel.IMMEDIATE 896 self.error_message_context = error_message_context 897 self.max_errors = max_errors 898 self._tokenizer = self.TOKENIZER_CLASS() 899 self.reset()
911 def parse( 912 self, raw_tokens: t.List[Token], sql: t.Optional[str] = None 913 ) -> t.List[t.Optional[exp.Expression]]: 914 """ 915 Parses a list of tokens and returns a list of syntax trees, one tree 916 per parsed SQL statement. 917 918 Args: 919 raw_tokens: The list of tokens. 920 sql: The original SQL string, used to produce helpful debug messages. 921 922 Returns: 923 The list of the produced syntax trees. 924 """ 925 return self._parse( 926 parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql 927 )
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.
929 def parse_into( 930 self, 931 expression_types: exp.IntoType, 932 raw_tokens: t.List[Token], 933 sql: t.Optional[str] = None, 934 ) -> t.List[t.Optional[exp.Expression]]: 935 """ 936 Parses a list of tokens into a given Expression type. If a collection of Expression 937 types is given instead, this method will try to parse the token list into each one 938 of them, stopping at the first for which the parsing succeeds. 939 940 Args: 941 expression_types: The expression type(s) to try and parse the token list into. 942 raw_tokens: The list of tokens. 943 sql: The original SQL string, used to produce helpful debug messages. 944 945 Returns: 946 The target Expression. 947 """ 948 errors = [] 949 for expression_type in ensure_list(expression_types): 950 parser = self.EXPRESSION_PARSERS.get(expression_type) 951 if not parser: 952 raise TypeError(f"No parser registered for {expression_type}") 953 954 try: 955 return self._parse(parser, raw_tokens, sql) 956 except ParseError as e: 957 e.errors[0]["into_expression"] = expression_type 958 errors.append(e) 959 960 raise ParseError( 961 f"Failed to parse '{sql or raw_tokens}' into {expression_types}", 962 errors=merge_errors(errors), 963 ) 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.
1000 def check_errors(self) -> None: 1001 """Logs or raises any found errors, depending on the chosen error level setting.""" 1002 if self.error_level == ErrorLevel.WARN: 1003 for error in self.errors: 1004 logger.error(str(error)) 1005 elif self.error_level == ErrorLevel.RAISE and self.errors: 1006 raise ParseError( 1007 concat_messages(self.errors, self.max_errors), 1008 errors=merge_errors(self.errors), 1009 )
Logs or raises any found errors, depending on the chosen error level setting.
1011 def raise_error(self, message: str, token: t.Optional[Token] = None) -> None: 1012 """ 1013 Appends an error in the list of recorded errors or raises it, depending on the chosen 1014 error level setting. 1015 """ 1016 token = token or self._curr or self._prev or Token.string("") 1017 start = token.start 1018 end = token.end + 1 1019 start_context = self.sql[max(start - self.error_message_context, 0) : start] 1020 highlight = self.sql[start:end] 1021 end_context = self.sql[end : end + self.error_message_context] 1022 1023 error = ParseError.new( 1024 f"{message}. Line {token.line}, Col: {token.col}.\n" 1025 f" {start_context}\033[4m{highlight}\033[0m{end_context}", 1026 description=message, 1027 line=token.line, 1028 col=token.col, 1029 start_context=start_context, 1030 highlight=highlight, 1031 end_context=end_context, 1032 ) 1033 1034 if self.error_level == ErrorLevel.IMMEDIATE: 1035 raise error 1036 1037 self.errors.append(error)
Appends an error in the list of recorded errors or raises it, depending on the chosen error level setting.
1039 def expression( 1040 self, exp_class: t.Type[E], comments: t.Optional[t.List[str]] = None, **kwargs 1041 ) -> E: 1042 """ 1043 Creates a new, validated Expression. 1044 1045 Args: 1046 exp_class: The expression class to instantiate. 1047 comments: An optional list of comments to attach to the expression. 1048 kwargs: The arguments to set for the expression along with their respective values. 1049 1050 Returns: 1051 The target expression. 1052 """ 1053 instance = exp_class(**kwargs) 1054 instance.add_comments(comments) if comments else self._add_comments(instance) 1055 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.
1062 def validate_expression(self, expression: E, args: t.Optional[t.List] = None) -> E: 1063 """ 1064 Validates an Expression, making sure that all its mandatory arguments are set. 1065 1066 Args: 1067 expression: The expression to validate. 1068 args: An optional list of items that was used to instantiate the expression, if it's a Func. 1069 1070 Returns: 1071 The validated expression. 1072 """ 1073 if self.error_level != ErrorLevel.IGNORE: 1074 for error_message in expression.error_messages(args): 1075 self.raise_error(error_message) 1076 1077 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.