1use alloc::boxed::Box;
13use alloc::format;
14use alloc::string::{String, ToString};
15use alloc::vec;
16use alloc::vec::Vec;
17use core::fmt;
18use core::mem;
19
20use crate::ast::{
21 AssignTarget, BinOp, CastTarget, Collation, ColumnDef, ColumnName, ColumnTypeName,
22 CreateFunctionStatement, CreateIndexStatement, CreatePublicationStatement,
23 CreateSubscriptionStatement, CreateTableStatement, CreateTriggerStatement, Expr, ExtractField,
24 FkAction, ForeignKeyConstraint, FrameBound, FrameKind, FromClause, FromJoin, FunctionArg,
25 FunctionArgMode, FunctionArgType, FunctionBody, FunctionReturn, IndexMethod, InsertStatement,
26 JoinKind, Literal, NullTreatment, OrderBy, PlPgSqlBlock, PlPgSqlDeclare, PlPgSqlStmt,
27 PublicationScope, RaiseLevel, RangeKindAst, ReturnTarget, SelectItem, SelectStatement,
28 Statement, TableRef, TriggerEvent, TriggerForEach, TriggerTiming, UnOp, UnionKind, VecEncoding,
29 WindowFrame,
30};
31use crate::lexer::{self, LexError, Token};
32
33fn is_dump_noise_statement(lc: &str) -> bool {
39 matches!(
40 lc,
41 "comment"
44 | "grant"
45 | "revoke"
46 | "lock"
48 | "unlock"
49 | "optimize"
53 | "check"
54 | "use"
55 | "\\restrict"
60 | "\\unrestrict"
61 | "delimiter"
70 )
71}
72
73fn unreserved_keyword_text(tok: &Token) -> Option<String> {
96 let s = match tok {
97 Token::Release => "release",
99 Token::Savepoint => "savepoint",
100 Token::Show => "show",
101 Token::Index => "index",
102 Token::Begin => "begin",
103 Token::Commit => "commit",
104 Token::Rollback => "rollback",
105 Token::Drop => "drop",
106 Token::Insert => "insert",
107 Token::Values => "values",
108 Token::Limit => "limit",
109 Token::Partition => "partition",
110 Token::Tables => "tables",
111 Token::Connection => "connection",
112 Token::Publication => "publication",
113 Token::Subscription => "subscription",
114 Token::Interval => "interval",
115 Token::Extract => "extract",
119 Token::Offset => "offset",
120 _ => return None,
129 };
130 Some(s.to_string())
131}
132
133fn is_vector_opclass_name(name: &str) -> bool {
141 let lc = name.to_ascii_lowercase();
142 matches!(
143 lc.as_str(),
144 "vector_cosine_ops"
145 | "vector_l2_ops"
146 | "vector_ip_ops"
147 | "halfvec_cosine_ops"
148 | "halfvec_l2_ops"
149 | "halfvec_ip_ops"
150 | "sq8_cosine_ops"
151 | "sq8_l2_ops"
152 | "sq8_ip_ops"
153 | "gin_trgm_ops"
159 | "gist_trgm_ops"
160 | "text_pattern_ops"
165 | "varchar_pattern_ops"
166 | "bpchar_pattern_ops"
167 | "int4_ops"
168 | "int8_ops"
169 | "text_ops"
170 )
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct ParseError {
175 pub message: String,
176 pub token_pos: usize,
178}
179
180impl fmt::Display for ParseError {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 write!(
183 f,
184 "parse error at token #{}: {}",
185 self.token_pos, self.message
186 )
187 }
188}
189
190impl From<LexError> for ParseError {
191 fn from(e: LexError) -> Self {
192 Self {
193 message: format!("lex: {e}"),
194 token_pos: 0,
195 }
196 }
197}
198
199pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
205 let tokens = lexer::tokenize(input)?;
206 let mut p = Parser::new(tokens);
207 let expr = p.parse_expr(0)?;
208 p.expect_eof()?;
209 Ok(expr)
210}
211
212pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
215 parse_statement_with(input, false)
216}
217
218pub fn parse_statement_with(input: &str, backslash_escapes: bool) -> Result<Statement, ParseError> {
222 let tokens = lexer::tokenize_with(input, backslash_escapes)?;
223 let mut p = Parser::new(tokens);
224 let stmt = p.parse_one_statement()?;
225 if matches!(p.peek(), Token::Semicolon) {
226 p.advance();
227 }
228 p.expect_eof()?;
229 Ok(stmt)
230}
231
232struct Parser {
233 tokens: Vec<Token>,
234 pos: usize,
235 nest_depth: usize,
241}
242
243const MAX_NEST_DEPTH: usize = 64;
250
251const MAX_BINARY_CHAIN: usize = 256;
257
258enum NamedTableConstraintKind {
262 Check,
263 Unique,
264 PrimaryKey,
265}
266
267impl Parser {
268 fn new(tokens: Vec<Token>) -> Self {
269 Self {
270 tokens,
271 pos: 0,
272 nest_depth: 0,
273 }
274 }
275
276 fn enter_nested(&mut self) -> Result<(), ParseError> {
279 self.nest_depth += 1;
280 if self.nest_depth > MAX_NEST_DEPTH {
281 self.nest_depth -= 1;
282 return Err(self.err(alloc::format!(
283 "statement nests deeper than {MAX_NEST_DEPTH} levels"
284 )));
285 }
286 Ok(())
287 }
288
289 fn peek(&self) -> &Token {
290 &self.tokens[self.pos]
292 }
293
294 fn advance(&mut self) -> Token {
295 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
296 if self.pos + 1 < self.tokens.len() {
297 self.pos += 1;
298 }
299 t
300 }
301
302 fn err(&self, message: String) -> ParseError {
303 ParseError {
304 message,
305 token_pos: self.pos,
306 }
307 }
308
309 fn expect_eof(&self) -> Result<(), ParseError> {
310 if matches!(self.peek(), Token::Eof) {
311 Ok(())
312 } else {
313 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
314 }
315 }
316
317 fn consume_until_statement_boundary(&mut self) {
322 loop {
323 match self.peek() {
324 Token::Semicolon | Token::Eof => return,
325 _ => self.advance(),
326 };
327 }
328 }
329
330 fn scan_sequence_name_until_boundary(&mut self) -> Option<String> {
336 let mut seq: Option<String> = None;
337 let mut after_sequence_kw = false;
338 let mut after_name_kw = false;
339 loop {
340 match self.peek().clone() {
341 Token::Semicolon | Token::Eof => break,
342 Token::Ident(s) | Token::QuotedIdent(s) => {
343 if after_name_kw && seq.is_none() {
344 self.advance();
345 let mut name = s;
346 while matches!(self.peek(), Token::Dot) {
349 self.advance();
350 if let Token::Ident(n) | Token::QuotedIdent(n) = self.advance() {
351 name = n;
352 }
353 }
354 seq = Some(name);
355 after_name_kw = false;
356 continue;
357 }
358 if after_sequence_kw && s.eq_ignore_ascii_case("name") {
359 after_name_kw = true;
360 after_sequence_kw = false;
361 } else {
362 after_sequence_kw = s.eq_ignore_ascii_case("sequence");
363 }
364 self.advance();
365 }
366 Token::String(s) => {
367 if seq.is_none() {
368 let bare = s
370 .rsplit_once('.')
371 .map_or_else(|| s.clone(), |(_, b)| b.to_string());
372 seq = Some(bare);
373 }
374 self.advance();
375 }
376 _ => {
377 after_sequence_kw = false;
378 after_name_kw = false;
379 self.advance();
380 }
381 }
382 }
383 seq
384 }
385
386 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
387 let first = match self.advance() {
388 Token::Ident(s) | Token::QuotedIdent(s) => s,
389 other if unreserved_keyword_text(&other).is_some() => {
401 unreserved_keyword_text(&other).unwrap()
402 }
403 other => {
404 return Err(ParseError {
405 message: format!("expected identifier, got {other:?}"),
406 token_pos: self.pos.saturating_sub(1),
407 });
408 }
409 };
410 if matches!(self.peek(), Token::Dot) {
417 self.advance();
418 match self.advance() {
419 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
420 other if unreserved_keyword_text(&other).is_some() => {
421 return Ok(unreserved_keyword_text(&other).unwrap());
422 }
423 other => {
424 return Err(ParseError {
425 message: format!("expected identifier after '{first}.', got {other:?}"),
426 token_pos: self.pos.saturating_sub(1),
427 });
428 }
429 }
430 }
431 Ok(first)
432 }
433
434 #[allow(clippy::too_many_lines)]
435 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
436 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
444 return Ok(Statement::Empty);
445 }
446 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
456 let lc = s.to_ascii_lowercase();
457 if is_dump_noise_statement(&lc) {
458 self.consume_until_statement_boundary();
459 return Ok(Statement::Empty);
460 }
461 }
462 match self.peek() {
463 Token::Select => self.parse_select_stmt(),
464 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
480 self.advance();
481 let body_text = match self.advance() {
482 Token::String(s) => s,
483 other => {
484 return Err(self.err(alloc::format!(
485 "expected dollar-quoted body after DO, got {other:?}"
486 )));
487 }
488 };
489 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
491 self.advance();
492 let _ = self.expect_ident_like()?;
493 }
494 let block = parse_plpgsql_body(&body_text)?;
499 Ok(Statement::DoBlock(block))
500 }
501 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
505 self.advance();
506 self.parse_with_cte_then_select()
507 }
508 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
511 self.advance();
512 let mut analyze = false;
513 let mut suggest = false;
514 let mut costs_off = false;
515 if matches!(self.peek(), Token::LParen) {
520 self.advance();
521 loop {
522 let opt = match self.peek().clone() {
523 Token::Ident(s) | Token::QuotedIdent(s) => s,
524 other => {
525 return Err(self.err(format!(
526 "expected option keyword inside EXPLAIN (…), got {other:?}"
527 )));
528 }
529 };
530 self.advance();
531 if opt.eq_ignore_ascii_case("suggest") {
532 suggest = true;
533 } else if opt.eq_ignore_ascii_case("costs") {
535 let value = match self.peek().clone() {
543 Token::On => {
544 self.advance();
545 true
546 }
547 Token::Ident(v) | Token::QuotedIdent(v)
548 if v.eq_ignore_ascii_case("off") =>
549 {
550 self.advance();
551 false
552 }
553 Token::Ident(v) | Token::QuotedIdent(v)
554 if v.eq_ignore_ascii_case("true") =>
555 {
556 self.advance();
557 true
558 }
559 _ => true,
560 };
561 costs_off = !value;
562 } else {
563 return Err(self.err(format!(
564 "unknown EXPLAIN option {opt:?}; v7.37.7 supports SUGGEST, COSTS"
565 )));
566 }
567 if matches!(self.peek(), Token::Comma) {
568 self.advance();
569 continue;
570 }
571 break;
572 }
573 if !matches!(self.peek(), Token::RParen) {
574 return Err(self.err(format!(
575 "expected ')' after EXPLAIN options, got {:?}",
576 self.peek()
577 )));
578 }
579 self.advance();
580 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
581 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
582 {
583 self.advance();
584 analyze = true;
585 }
586 let inner = self.parse_select_stmt()?;
587 let Statement::Select(s) = inner else {
588 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
589 };
590 Ok(Statement::Explain(crate::ast::ExplainStatement {
591 analyze,
592 inner: Box::new(s),
593 suggest,
594 costs_off,
595 }))
596 }
597 Token::Create => self.parse_create_stmt(),
598 Token::Insert => self.parse_insert_stmt(),
599 Token::Begin => {
600 self.advance();
601 Ok(Statement::Begin)
602 }
603 Token::Commit => {
604 self.advance();
605 Ok(Statement::Commit)
606 }
607 Token::Rollback => {
608 self.advance();
609 if matches!(self.peek(), Token::To) {
613 self.advance();
614 if matches!(self.peek(), Token::Savepoint) {
615 self.advance();
616 }
617 let name = self.expect_ident_like()?;
618 Ok(Statement::RollbackToSavepoint(name))
619 } else {
620 Ok(Statement::Rollback)
621 }
622 }
623 Token::Savepoint => {
624 self.advance();
625 let name = self.expect_ident_like()?;
626 Ok(Statement::Savepoint(name))
627 }
628 Token::Release => {
629 self.advance();
630 if matches!(self.peek(), Token::Savepoint) {
633 self.advance();
634 }
635 let name = self.expect_ident_like()?;
636 Ok(Statement::ReleaseSavepoint(name))
637 }
638 Token::Show => {
639 self.advance();
640 let target = match self.advance() {
646 Token::Tables => "tables".to_string(),
647 Token::Create => "create".to_string(),
651 Token::Index => "index".to_string(),
654 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
655 other => {
656 return Err(self.err(format!(
657 "expected SHOW target, got {other:?}"
658 )));
659 }
660 };
661 match target.as_str() {
662 "tables" => Ok(Statement::ShowTables),
663 "users" => Ok(Statement::ShowUsers),
664 "indexes" | "index" | "keys" => {
674 if !matches!(self.peek(), Token::From) {
675 return Err(self.err(format!(
676 "expected FROM after SHOW INDEXES, got {:?}",
677 self.peek()
678 )));
679 }
680 self.advance();
681 let table = self.expect_ident_like()?;
682 Ok(Statement::ShowIndexes(table))
683 }
684 "status" => Ok(Statement::ShowStatus),
689 "variables" => Ok(Statement::ShowVariables),
690 "processlist" => Ok(Statement::ShowProcesslist),
692 "create" => {
693 let kind = match self.advance() {
696 Token::Ident(s) | Token::QuotedIdent(s) => s,
697 Token::Table => "table".to_string(),
698 other => {
699 return Err(self.err(format!(
700 "expected TABLE after SHOW CREATE, got {other:?}"
701 )));
702 }
703 };
704 if !kind.eq_ignore_ascii_case("table") {
705 return Err(self.err(format!(
706 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
707 )));
708 }
709 let name = self.expect_ident_like()?;
710 Ok(Statement::ShowCreateTable(name))
711 }
712 "databases" | "schemas" => Ok(Statement::ShowDatabases),
718 "publications" => Ok(Statement::ShowPublications),
723 "subscriptions" => Ok(Statement::ShowSubscriptions),
725 "columns" => {
726 if !matches!(self.peek(), Token::From) {
727 return Err(self.err(format!(
728 "expected FROM after SHOW COLUMNS, got {:?}",
729 self.peek()
730 )));
731 }
732 self.advance();
733 let table = self.expect_ident_like()?;
734 Ok(Statement::ShowColumns(table))
735 }
736 other => Err(self.err(format!(
737 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
738 ))),
739 }
740 }
741 Token::Drop => {
747 self.advance();
748 match self.peek() {
749 Token::Publication => {
750 self.advance();
751 let name = self.expect_ident_or_string()?;
752 Ok(Statement::DropPublication(name))
753 }
754 Token::Subscription => {
755 self.advance();
756 let name = self.expect_ident_or_string()?;
757 Ok(Statement::DropSubscription(name))
758 }
759 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
760 self.advance();
761 let name = self.expect_ident_or_string()?;
762 Ok(Statement::DropUser(name))
763 }
764 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
766 self.advance();
767 let if_exists = self.consume_if_exists();
768 let name = self.expect_ident_like()?;
769 if !matches!(self.peek(), Token::On) {
771 return Err(self.err(alloc::format!(
772 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
773 self.peek()
774 )));
775 }
776 self.advance();
777 let table = self.expect_ident_like()?;
778 Ok(Statement::DropTrigger {
779 name,
780 table,
781 if_exists,
782 })
783 }
784 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
788 self.advance();
789 let if_exists = self.consume_if_exists();
790 let name = self.expect_ident_like()?;
791 if matches!(self.peek(), Token::LParen) {
793 self.advance();
794 let mut depth = 1usize;
796 while depth > 0 {
797 match self.peek() {
798 Token::LParen => depth += 1,
799 Token::RParen => depth -= 1,
800 Token::Eof => {
801 return Err(self.err(alloc::format!(
802 "unterminated arg list in DROP FUNCTION {name:?}"
803 )));
804 }
805 _ => {}
806 }
807 self.advance();
808 }
809 }
810 Ok(Statement::DropFunction { name, if_exists })
811 }
812 Token::Table => {
820 self.advance();
821 let if_exists = self.consume_if_exists();
822 let mut names: Vec<String> = Vec::new();
823 loop {
824 names.push(self.expect_ident_like()?);
825 if matches!(self.peek(), Token::Comma) {
826 self.advance();
827 continue;
828 }
829 break;
830 }
831 if matches!(
832 self.peek(),
833 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
834 || s.eq_ignore_ascii_case("restrict")
835 ) {
836 self.advance();
837 }
838 Ok(Statement::DropTable { names, if_exists })
839 }
840 Token::Index => {
846 self.advance();
847 let if_exists = self.consume_if_exists();
848 let name = self.expect_ident_like()?;
849 if matches!(
850 self.peek(),
851 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
852 || s.eq_ignore_ascii_case("restrict")
853 ) {
854 self.advance();
855 }
856 Ok(Statement::DropIndex { name, if_exists })
857 }
858 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
864 self.advance();
865 let if_exists = self.consume_if_exists();
866 let mut names = vec![self.expect_ident_like()?];
867 while matches!(self.peek(), Token::Comma) {
868 self.advance();
869 names.push(self.expect_ident_like()?);
870 }
871 if matches!(
872 self.peek(),
873 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
874 || s.eq_ignore_ascii_case("restrict")
875 ) {
876 self.advance();
877 }
878 Ok(Statement::DropSchema { names, if_exists })
879 }
880 Token::Ident(s) | Token::QuotedIdent(s)
883 if s.eq_ignore_ascii_case("type") =>
884 {
885 self.advance();
886 let if_exists = self.consume_if_exists();
887 let mut names = vec![self.expect_ident_like()?];
888 while matches!(self.peek(), Token::Comma) {
889 self.advance();
890 names.push(self.expect_ident_like()?);
891 }
892 if matches!(
893 self.peek(),
894 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
895 || s.eq_ignore_ascii_case("restrict")
896 ) {
897 self.advance();
898 }
899 Ok(Statement::DropType { names, if_exists })
900 }
901 Token::Ident(s) | Token::QuotedIdent(s)
904 if s.eq_ignore_ascii_case("domain") =>
905 {
906 self.advance();
907 let if_exists = self.consume_if_exists();
908 let mut names = vec![self.expect_ident_like()?];
909 while matches!(self.peek(), Token::Comma) {
910 self.advance();
911 names.push(self.expect_ident_like()?);
912 }
913 if matches!(
914 self.peek(),
915 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
916 || s.eq_ignore_ascii_case("restrict")
917 ) {
918 self.advance();
919 }
920 Ok(Statement::DropDomain { names, if_exists })
921 }
922 Token::Ident(s) | Token::QuotedIdent(s)
925 if s.eq_ignore_ascii_case("materialized") =>
926 {
927 self.advance();
928 let nxt = self.peek().clone();
929 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
930 {
931 return Err(self.err(alloc::format!(
932 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
933 )));
934 }
935 self.advance();
936 let if_exists = self.consume_if_exists();
937 let mut names = vec![self.expect_ident_like()?];
938 while matches!(self.peek(), Token::Comma) {
939 self.advance();
940 names.push(self.expect_ident_like()?);
941 }
942 if matches!(
943 self.peek(),
944 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
945 || s.eq_ignore_ascii_case("restrict")
946 ) {
947 self.advance();
948 }
949 Ok(Statement::DropMaterializedView { names, if_exists })
950 }
951 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
954 self.advance();
955 let if_exists = self.consume_if_exists();
956 let mut names = vec![self.expect_ident_like()?];
957 while matches!(self.peek(), Token::Comma) {
958 self.advance();
959 names.push(self.expect_ident_like()?);
960 }
961 if matches!(
962 self.peek(),
963 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
964 || s.eq_ignore_ascii_case("restrict")
965 ) {
966 self.advance();
967 }
968 Ok(Statement::DropView { names, if_exists })
969 }
970 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
974 self.advance();
975 let if_exists = self.consume_if_exists();
976 let mut names = vec![self.expect_ident_like()?];
977 while matches!(self.peek(), Token::Comma) {
978 self.advance();
979 names.push(self.expect_ident_like()?);
980 }
981 if matches!(
982 self.peek(),
983 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
984 || s.eq_ignore_ascii_case("restrict")
985 ) {
986 self.advance();
987 }
988 Ok(Statement::DropSequence { names, if_exists })
989 }
990 other => Err(self.err(format!(
991 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
992 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
993 ))),
994 }
995 }
996 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
998 self.advance();
999 let nxt = self.peek().clone();
1000 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
1001 {
1002 return Err(self.err(alloc::format!(
1003 "expected MATERIALIZED after REFRESH, got {nxt:?}"
1004 )));
1005 }
1006 self.advance();
1007 let nxt2 = self.peek().clone();
1008 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1009 {
1010 return Err(self.err(alloc::format!(
1011 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
1012 )));
1013 }
1014 self.advance();
1015 let name = self.expect_ident_like()?;
1016 let with_data = self.parse_optional_with_data(true)?;
1017 Ok(Statement::RefreshMaterializedView { name, with_data })
1018 }
1019 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1020 self.advance();
1021 self.parse_update_after_keyword()
1022 }
1023 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1024 self.advance();
1025 self.parse_delete_after_keyword()
1026 }
1027 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
1031 self.advance();
1032 self.parse_alter_after_keyword()
1033 }
1034 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
1038 self.advance();
1039 self.parse_wait_after_keyword()
1040 }
1041 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
1052 self.advance();
1053 let next = self.peek().clone();
1054 let cold = match next {
1055 Token::Ident(s) | Token::QuotedIdent(s) => s,
1056 _ => {
1057 return Err(
1058 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
1059 );
1060 }
1061 };
1062 if !cold.eq_ignore_ascii_case("cold") {
1063 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
1064 }
1065 self.advance();
1066 let next = self.peek().clone();
1067 let segments = match next {
1068 Token::Ident(s) | Token::QuotedIdent(s) => s,
1069 _ => {
1070 return Err(self.err(format!(
1071 "expected SEGMENTS after COMPACT COLD, got {:?}",
1072 self.peek()
1073 )));
1074 }
1075 };
1076 if !segments.eq_ignore_ascii_case("segments") {
1077 return Err(self.err(format!(
1078 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
1079 )));
1080 }
1081 self.advance();
1082 Ok(Statement::CompactColdSegments)
1083 }
1084 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
1091 self.advance();
1092 self.parse_merge_after_keyword()
1093 }
1094 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
1095 self.advance();
1096 let target = match self.peek() {
1097 Token::Eof | Token::Semicolon => None,
1098 Token::Ident(_) | Token::QuotedIdent(_) => {
1099 Some(self.expect_ident_like()?)
1100 }
1101 other => {
1102 return Err(self.err(format!(
1103 "expected table name or end of statement after ANALYZE, got {other:?}"
1104 )));
1105 }
1106 };
1107 Ok(Statement::Analyze(target))
1108 }
1109 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
1115 self.advance();
1116 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("local") || s.eq_ignore_ascii_case("session") || s.eq_ignore_ascii_case("global"))
1121 {
1122 self.advance();
1123 }
1124 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
1129 {
1130 self.advance();
1131 if matches!(
1133 self.peek(),
1134 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1135 ) {
1136 self.advance();
1137 }
1138 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
1140 {
1141 self.advance();
1142 if matches!(
1143 self.peek(),
1144 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1145 ) {
1146 self.advance();
1147 }
1148 }
1149 return Ok(Statement::Empty);
1150 }
1151 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
1161 {
1162 self.advance(); match self.peek().clone() {
1164 Token::Default => {
1165 self.advance();
1166 }
1167 Token::String(_)
1168 | Token::Ident(_)
1169 | Token::QuotedIdent(_) => {
1170 self.advance();
1171 }
1172 other => {
1173 return Err(self.err(alloc::format!(
1174 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
1175 )));
1176 }
1177 }
1178 return Ok(Statement::Empty);
1179 }
1180 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
1183 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
1184 {
1185 self.advance(); self.advance(); if matches!(
1188 self.peek(),
1189 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1190 ) {
1191 self.advance();
1192 }
1193 return Ok(Statement::Empty);
1194 }
1195 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
1200 loop {
1201 let lhs = match self.peek().clone() {
1202 Token::SessionVar(s) => {
1203 self.advance();
1204 s
1205 }
1206 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
1207 other => {
1208 return Err(self.err(format!(
1209 "expected parameter name after SET, got {other:?}"
1210 )));
1211 }
1212 };
1213 match self.peek() {
1215 Token::Eq => {
1216 self.advance();
1217 }
1218 Token::To => {
1219 self.advance();
1220 }
1221 other => {
1222 return Err(self.err(format!(
1223 "expected `=` or TO after SET {lhs}, got {other:?}"
1224 )));
1225 }
1226 }
1227 let value = self.parse_set_value()?;
1228 pairs.push((lhs, value));
1229 if matches!(self.peek(), Token::Comma) {
1230 self.advance();
1231 continue;
1232 }
1233 break;
1234 }
1235 if pairs.len() == 1 {
1236 let (name, value) = pairs.into_iter().next().unwrap();
1237 Ok(Statement::SetParameter { name, value })
1238 } else {
1239 Ok(Statement::SetParameterList(pairs))
1240 }
1241 }
1242 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1244 self.advance();
1245 match self.peek().clone() {
1246 Token::All => {
1247 self.advance();
1248 Ok(Statement::ResetParameter(None))
1249 }
1250 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1251 self.advance();
1252 Ok(Statement::ResetParameter(None))
1253 }
1254 _ => {
1255 let name = self.parse_set_param_name()?;
1256 Ok(Statement::ResetParameter(Some(name)))
1257 }
1258 }
1259 }
1260 other => Err(self.err(format!(
1261 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1262 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1263 ))),
1264 }
1265 }
1266
1267 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1268 debug_assert!(matches!(self.peek(), Token::Create));
1269 self.advance();
1270 match self.peek() {
1271 Token::Table => self.parse_create_table_stmt_after_create(),
1272 Token::Index => self.parse_create_index_stmt_after_create(false),
1273 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1280 self.advance();
1281 if !matches!(self.peek(), Token::Index) {
1282 return Err(self.err(alloc::format!(
1283 "expected INDEX after CREATE UNIQUE, got {:?}",
1284 self.peek()
1285 )));
1286 }
1287 self.parse_create_index_stmt_after_create(true)
1288 }
1289 Token::Publication => {
1290 self.advance();
1291 self.parse_create_publication_after_keyword()
1292 }
1293 Token::Subscription => {
1294 self.advance();
1295 self.parse_create_subscription_after_keyword()
1296 }
1297 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1301 self.advance();
1302 self.parse_create_user_after_keyword()
1303 }
1304 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1308 self.advance();
1309 self.parse_create_extension_after_keyword()
1310 }
1311 Token::Or => {
1317 self.advance();
1318 let next = self.peek();
1319 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1320 return Err(self.err(alloc::format!(
1321 "expected REPLACE after CREATE OR, got {next:?}"
1322 )));
1323 };
1324 if !s2.eq_ignore_ascii_case("replace") {
1325 return Err(self.err(alloc::format!(
1326 "expected REPLACE after CREATE OR, got {s2:?}"
1327 )));
1328 }
1329 self.advance();
1330 self.parse_create_function_or_trigger_after_or_replace(true)
1331 }
1332 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1333 self.advance();
1334 self.parse_create_function_after_keyword(false)
1335 }
1336 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1337 self.advance();
1338 self.parse_create_trigger_after_keyword(false)
1339 }
1340 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1342 self.advance();
1343 self.parse_create_sequence_after_keyword(false)
1344 }
1345 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1347 self.advance();
1348 self.parse_create_view_after_keyword(false, false, false)
1349 }
1350 Token::Ident(s) | Token::QuotedIdent(s)
1364 if s.eq_ignore_ascii_case("algorithm")
1365 || s.eq_ignore_ascii_case("definer")
1366 || s.eq_ignore_ascii_case("sql") =>
1367 {
1368 self.consume_mysql_view_prefix()?;
1369 let next = self.peek().clone();
1374 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1375 if s2.eq_ignore_ascii_case("view"))
1376 {
1377 self.advance();
1378 self.parse_create_view_after_keyword(false, false, false)
1379 } else {
1380 Err(self.err(alloc::format!(
1381 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1382 )))
1383 }
1384 }
1385 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1387 self.advance();
1388 self.parse_create_type_after_keyword()
1389 }
1390 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1393 self.advance();
1394 self.parse_create_domain_after_keyword()
1395 }
1396 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1400 self.advance();
1401 let if_not_exists = self.parse_if_not_exists();
1402 let name = self.expect_ident_like()?;
1403 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1406 if s.eq_ignore_ascii_case("authorization"))
1407 {
1408 self.advance();
1409 let _ = self.expect_ident_like()?;
1410 }
1411 Ok(Statement::CreateSchema { name, if_not_exists })
1412 }
1413 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1415 self.advance();
1416 let next = self.peek().clone();
1417 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1418 {
1419 self.advance();
1420 self.parse_create_materialized_view_after_keyword()
1421 } else {
1422 Err(self.err(alloc::format!(
1423 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1424 )))
1425 }
1426 }
1427 Token::Ident(s) | Token::QuotedIdent(s)
1428 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1429 {
1430 self.advance();
1431 let next = self.peek().clone();
1433 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1434 {
1435 self.advance();
1436 self.parse_create_sequence_after_keyword(true)
1437 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1438 {
1439 self.advance();
1440 self.parse_create_view_after_keyword(false, false, true)
1441 } else {
1442 self.consume_until_statement_boundary();
1444 Ok(Statement::Empty)
1445 }
1446 }
1447 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1457 self.consume_mysql_routine_body();
1458 Ok(Statement::Empty)
1459 }
1460 Token::Ident(s) | Token::QuotedIdent(s)
1471 if matches!(
1472 s.to_ascii_lowercase().as_str(),
1473 "database"
1474 | "role"
1475 | "policy"
1476 | "operator"
1477 | "cast"
1478 | "rule"
1479 | "aggregate"
1480 | "language"
1481 | "collation"
1482 | "conversion"
1483 | "statistics"
1491 | "event"
1492 | "foreign"
1493 ) =>
1494 {
1495 self.consume_until_statement_boundary();
1496 Ok(Statement::Empty)
1497 }
1498 other => Err(self.err(format!(
1499 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1500 ))),
1501 }
1502 }
1503
1504 fn parse_create_function_or_trigger_after_or_replace(
1509 &mut self,
1510 or_replace: bool,
1511 ) -> Result<Statement, ParseError> {
1512 let tok = self.peek();
1513 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1514 return Err(self.err(alloc::format!(
1515 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1516 )));
1517 };
1518 if s.eq_ignore_ascii_case("function") {
1519 self.advance();
1520 self.parse_create_function_after_keyword(or_replace)
1521 } else if s.eq_ignore_ascii_case("trigger") {
1522 self.advance();
1523 self.parse_create_trigger_after_keyword(or_replace)
1524 } else if s.eq_ignore_ascii_case("view") {
1525 self.advance();
1527 self.parse_create_view_after_keyword(or_replace, false, false)
1528 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1529 self.advance();
1531 let nxt = self.peek().clone();
1532 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1533 {
1534 self.advance();
1535 self.parse_create_view_after_keyword(or_replace, false, true)
1536 } else {
1537 Err(self.err(alloc::format!(
1538 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1539 )))
1540 }
1541 } else {
1542 Err(self.err(alloc::format!(
1543 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1544 )))
1545 }
1546 }
1547
1548 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1553 self.consume_if_not_exists();
1555 let name = self.expect_ident_like()?;
1556 loop {
1559 match self.peek() {
1560 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1561 self.advance();
1562 continue;
1563 }
1564 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1565 self.advance();
1566 let _ = self.expect_ident_like()?;
1567 continue;
1568 }
1569 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1570 self.advance();
1571 let _ = self.advance();
1573 continue;
1574 }
1575 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1576 self.advance();
1577 let _ = self.advance();
1578 continue;
1579 }
1580 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1581 self.advance();
1582 continue;
1583 }
1584 _ => break,
1585 }
1586 }
1587 Ok(Statement::CreateExtension(name))
1588 }
1589
1590 fn parse_create_function_after_keyword(
1602 &mut self,
1603 or_replace: bool,
1604 ) -> Result<Statement, ParseError> {
1605 let name = self.expect_ident_like()?;
1606 if !matches!(self.peek(), Token::LParen) {
1610 return Err(self.err(alloc::format!(
1611 "expected '(' after function name {name:?}, got {:?}",
1612 self.peek()
1613 )));
1614 }
1615 self.advance();
1616 let args = self.parse_function_arg_list()?;
1617 let tok = self.peek();
1619 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1620 return Err(self.err(alloc::format!(
1621 "expected RETURNS after function arg list, got {tok:?}"
1622 )));
1623 };
1624 if !s.eq_ignore_ascii_case("returns") {
1625 return Err(self.err(alloc::format!(
1626 "expected RETURNS after function arg list, got {s:?}"
1627 )));
1628 }
1629 self.advance();
1630 let returns = self.parse_function_return()?;
1631 let mut language: Option<String> = self.parse_optional_language()?;
1634 if !matches!(self.peek(), Token::As) {
1638 return Err(self.err(alloc::format!(
1639 "expected AS before function body, got {:?}",
1640 self.peek()
1641 )));
1642 }
1643 self.advance();
1644 let body_text = match self.peek() {
1645 Token::String(s) => {
1646 let body = s.clone();
1647 self.advance();
1648 body
1649 }
1650 other => {
1651 return Err(self.err(alloc::format!(
1652 "expected $$-quoted function body after AS, got {other:?}"
1653 )));
1654 }
1655 };
1656 if language.is_none() {
1658 language = self.parse_optional_language()?;
1659 }
1660 let language = language.unwrap_or_else(|| String::from("sql"));
1661 let body = if language.eq_ignore_ascii_case("plpgsql") {
1666 match parse_plpgsql_body(&body_text) {
1667 Ok(block) => FunctionBody::PlPgSql(block),
1668 Err(_) => FunctionBody::Raw(body_text),
1674 }
1675 } else {
1676 FunctionBody::Raw(body_text)
1677 };
1678 Ok(Statement::CreateFunction(CreateFunctionStatement {
1679 name,
1680 or_replace,
1681 args,
1682 returns,
1683 language,
1684 body,
1685 }))
1686 }
1687
1688 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1692 let mut args: Vec<FunctionArg> = Vec::new();
1693 if matches!(self.peek(), Token::RParen) {
1694 self.advance();
1695 return Ok(args);
1696 }
1697 loop {
1698 let mode = if matches!(self.peek(), Token::In) {
1701 self.advance();
1702 FunctionArgMode::In
1703 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1704 {
1705 self.advance();
1706 FunctionArgMode::Out
1707 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1708 {
1709 self.advance();
1710 FunctionArgMode::InOut
1711 } else {
1712 FunctionArgMode::In
1713 };
1714 let (name, ty_token) = {
1720 let first = self.expect_ident_like()?;
1721 match self.peek() {
1724 Token::Ident(_) | Token::QuotedIdent(_) => {
1725 let ty = self.expect_ident_like()?;
1726 (Some(first), ty)
1727 }
1728 _ => (None, first),
1729 }
1730 };
1731 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1733 Some(t) => FunctionArgType::Typed(t),
1734 None => FunctionArgType::Raw(ty_token),
1735 };
1736 args.push(FunctionArg { mode, name, ty });
1737 match self.peek() {
1738 Token::Comma => {
1739 self.advance();
1740 continue;
1741 }
1742 Token::RParen => {
1743 self.advance();
1744 return Ok(args);
1745 }
1746 other => {
1747 return Err(self.err(alloc::format!(
1748 "expected , or ) in function arg list, got {other:?}"
1749 )));
1750 }
1751 }
1752 }
1753 }
1754
1755 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1756 let ident = self.expect_ident_like()?;
1757 if ident.eq_ignore_ascii_case("trigger") {
1758 return Ok(FunctionReturn::Trigger);
1759 }
1760 if ident.eq_ignore_ascii_case("void") {
1761 return Ok(FunctionReturn::Void);
1762 }
1763 match map_type_ident_to_column_type_name(&ident) {
1764 Some(t) => Ok(FunctionReturn::Type(t)),
1765 None => Ok(FunctionReturn::Other(ident)),
1766 }
1767 }
1768
1769 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1770 match self.peek() {
1771 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1772 self.advance();
1773 let lang = self.expect_ident_like()?;
1774 Ok(Some(lang.to_ascii_lowercase()))
1775 }
1776 _ => Ok(None),
1777 }
1778 }
1779
1780 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1786 let name = self.expect_ident_like()?;
1787 if matches!(self.peek(), Token::As) {
1789 self.advance();
1790 }
1791 let base_type = self.parse_column_type_name()?;
1792 let mut default: Option<Expr> = None;
1793 let mut not_null = false;
1794 let mut checks: Vec<Expr> = Vec::new();
1795 loop {
1796 match self.peek() {
1797 Token::Default => {
1798 if default.is_some() {
1799 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1800 }
1801 self.advance();
1802 default = Some(self.parse_expr(0)?);
1803 }
1804 Token::Not => {
1805 self.advance();
1806 if !matches!(self.peek(), Token::Null) {
1807 return Err(self.err(alloc::format!(
1808 "expected NULL after NOT in DOMAIN, got {:?}",
1809 self.peek()
1810 )));
1811 }
1812 self.advance();
1813 not_null = true;
1814 }
1815 Token::Null => {
1816 self.advance();
1817 }
1821 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1822 self.advance();
1823 if !matches!(self.peek(), Token::LParen) {
1824 return Err(self.err(alloc::format!(
1825 "expected '(' after CHECK in DOMAIN, got {:?}",
1826 self.peek()
1827 )));
1828 }
1829 self.advance();
1830 let expr = self.parse_expr(0)?;
1831 if !matches!(self.peek(), Token::RParen) {
1832 return Err(self.err(alloc::format!(
1833 "expected ')' after CHECK expr, got {:?}",
1834 self.peek()
1835 )));
1836 }
1837 self.advance();
1838 checks.push(expr);
1839 }
1840 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1844 self.advance();
1845 let _ = self.expect_ident_like()?;
1846 }
1847 _ => break,
1848 }
1849 }
1850 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1851 name,
1852 base_type,
1853 default,
1854 not_null,
1855 checks,
1856 }))
1857 }
1858
1859 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1863 let name = self.expect_ident_like()?;
1864 if !matches!(self.peek(), Token::As) {
1866 return Err(self.err(alloc::format!(
1867 "expected AS after CREATE TYPE {name:?}, got {:?}",
1868 self.peek()
1869 )));
1870 }
1871 self.advance();
1872 if matches!(self.peek(), Token::LParen) {
1876 self.advance();
1877 let mut fields: Vec<(String, ColumnTypeName)> = Vec::new();
1878 loop {
1879 let field_name = self.expect_ident_like()?;
1880 let field_type = self.parse_column_type_name()?;
1881 fields.push((field_name, field_type));
1882 if matches!(self.peek(), Token::Comma) {
1883 self.advance();
1884 continue;
1885 }
1886 if matches!(self.peek(), Token::RParen) {
1887 self.advance();
1888 break;
1889 }
1890 return Err(self.err(alloc::format!(
1891 "expected , or ) in composite field list, got {:?}",
1892 self.peek()
1893 )));
1894 }
1895 if fields.is_empty() {
1896 return Err(self.err("CREATE TYPE … AS (…) must declare at least one field".into()));
1897 }
1898 return Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1899 name,
1900 kind: crate::ast::TypeKind::Composite { fields },
1901 }));
1902 }
1903 let kind_ident = match self.peek().clone() {
1905 Token::Ident(s) | Token::QuotedIdent(s) => s,
1906 other => {
1907 return Err(self.err(alloc::format!(
1908 "expected ENUM or '(' after CREATE TYPE {name:?} AS, got {other:?}"
1909 )));
1910 }
1911 };
1912 if !kind_ident.eq_ignore_ascii_case("enum") {
1913 return Err(self.err(alloc::format!(
1914 "Phase 1.4 only supports ENUM or composite '(…)'; got {kind_ident:?}"
1915 )));
1916 }
1917 self.advance();
1918 if !matches!(self.peek(), Token::LParen) {
1919 return Err(self.err(alloc::format!(
1920 "expected '(' after ENUM, got {:?}",
1921 self.peek()
1922 )));
1923 }
1924 self.advance();
1925 let mut labels: Vec<String> = Vec::new();
1926 loop {
1927 match self.peek().clone() {
1928 Token::String(s) => {
1929 self.advance();
1930 labels.push(s);
1931 }
1932 other => {
1933 return Err(
1934 self.err(alloc::format!("expected enum label string, got {other:?}"))
1935 );
1936 }
1937 }
1938 if matches!(self.peek(), Token::Comma) {
1939 self.advance();
1940 continue;
1941 }
1942 if matches!(self.peek(), Token::RParen) {
1943 self.advance();
1944 break;
1945 }
1946 return Err(self.err(alloc::format!(
1947 "expected , or ) in ENUM label list, got {:?}",
1948 self.peek()
1949 )));
1950 }
1951 if labels.is_empty() {
1952 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
1953 }
1954 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1955 name,
1956 kind: crate::ast::TypeKind::Enum { labels },
1957 }))
1958 }
1959
1960 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
1965 let if_not_exists = self.parse_if_not_exists();
1966 let name = self.expect_ident_like()?;
1967 let mut columns: Vec<String> = Vec::new();
1968 if matches!(self.peek(), Token::LParen) {
1969 self.advance();
1970 loop {
1971 let c = self.expect_ident_like()?;
1972 columns.push(c);
1973 if matches!(self.peek(), Token::Comma) {
1974 self.advance();
1975 continue;
1976 }
1977 if matches!(self.peek(), Token::RParen) {
1978 self.advance();
1979 break;
1980 }
1981 return Err(self.err(alloc::format!(
1982 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
1983 self.peek()
1984 )));
1985 }
1986 }
1987 if !matches!(self.peek(), Token::As) {
1988 return Err(self.err(alloc::format!(
1989 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
1990 self.peek()
1991 )));
1992 }
1993 self.advance();
1994 let body_stmt = self.parse_select_stmt()?;
1995 let Statement::Select(body) = body_stmt else {
1996 return Err(self.err(alloc::format!(
1997 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
1998 )));
1999 };
2000 let with_data = self.parse_optional_with_data(true)?;
2002 Ok(Statement::CreateMaterializedView(
2003 crate::ast::CreateMaterializedViewStatement {
2004 name,
2005 if_not_exists,
2006 columns,
2007 body,
2008 with_data,
2009 },
2010 ))
2011 }
2012
2013 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
2018 let save = self.pos;
2019 let is_with = match self.peek() {
2021 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
2022 _ => false,
2023 };
2024 if !is_with {
2025 return Ok(default_when_absent);
2026 }
2027 self.advance();
2028 let mut with_data = true;
2030 let is_no = match self.peek() {
2031 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
2032 _ => false,
2033 };
2034 if is_no {
2035 self.advance();
2036 with_data = false;
2037 }
2038 let is_data = match self.peek() {
2040 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
2041 _ => false,
2042 };
2043 if is_data {
2044 self.advance();
2045 Ok(with_data)
2046 } else {
2047 self.pos = save;
2050 Ok(default_when_absent)
2051 }
2052 }
2053
2054 fn parse_create_view_after_keyword(
2059 &mut self,
2060 or_replace: bool,
2061 _materialized_unused: bool,
2062 temporary: bool,
2063 ) -> Result<Statement, ParseError> {
2064 let if_not_exists = self.parse_if_not_exists();
2065 let name = self.expect_ident_like()?;
2066 let mut columns: Vec<String> = Vec::new();
2068 if matches!(self.peek(), Token::LParen) {
2069 self.advance();
2070 loop {
2071 let c = self.expect_ident_like()?;
2072 columns.push(c);
2073 if matches!(self.peek(), Token::Comma) {
2074 self.advance();
2075 continue;
2076 }
2077 if matches!(self.peek(), Token::RParen) {
2078 self.advance();
2079 break;
2080 }
2081 return Err(self.err(alloc::format!(
2082 "expected , or ) in VIEW column list, got {:?}",
2083 self.peek()
2084 )));
2085 }
2086 }
2087 if !matches!(self.peek(), Token::As) {
2089 return Err(self.err(alloc::format!(
2090 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
2091 self.peek()
2092 )));
2093 }
2094 self.advance();
2095 let body_stmt = self.parse_select_stmt()?;
2097 let Statement::Select(body) = body_stmt else {
2098 return Err(self.err(alloc::format!(
2099 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
2100 )));
2101 };
2102 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
2103 name,
2104 or_replace,
2105 if_not_exists,
2106 temporary,
2107 columns,
2108 body,
2109 }))
2110 }
2111
2112 fn parse_create_sequence_after_keyword(
2116 &mut self,
2117 temporary: bool,
2118 ) -> Result<Statement, ParseError> {
2119 let if_not_exists = self.parse_if_not_exists();
2120 let name = self.expect_ident_like()?;
2121 let data_type = if matches!(self.peek(), Token::As) {
2123 self.advance();
2124 Some(self.parse_sequence_data_type()?)
2125 } else {
2126 None
2127 };
2128 let options = self.parse_sequence_options(false)?;
2129 Ok(Statement::CreateSequence(
2130 crate::ast::CreateSequenceStatement {
2131 name,
2132 if_not_exists,
2133 temporary,
2134 data_type,
2135 options,
2136 },
2137 ))
2138 }
2139
2140 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
2143 let if_exists = self.parse_if_exists();
2144 let name = self.expect_ident_like()?;
2145 let options = self.parse_sequence_options(true)?;
2146 Ok(Statement::AlterSequence(
2147 crate::ast::AlterSequenceStatement {
2148 name,
2149 if_exists,
2150 options,
2151 },
2152 ))
2153 }
2154
2155 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
2156 let kw = self.expect_ident_like()?;
2157 match kw.to_ascii_lowercase().as_str() {
2158 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
2159 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
2160 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
2161 other => Err(self.err(alloc::format!(
2162 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
2163 ))),
2164 }
2165 }
2166
2167 fn parse_sequence_options(
2168 &mut self,
2169 allow_restart: bool,
2170 ) -> Result<crate::ast::SequenceOptions, ParseError> {
2171 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
2172 let mut opts = SequenceOptions::default();
2173 #[allow(clippy::while_let_loop)]
2174 loop {
2175 let kw_lc = match self.peek() {
2178 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
2179 _ => break,
2180 };
2181 match kw_lc.as_str() {
2182 "increment" => {
2183 self.advance();
2184 if matches!(self.peek(), Token::By) {
2186 self.advance();
2187 }
2188 opts.increment = Some(self.expect_signed_int()?);
2189 }
2190 "minvalue" => {
2191 self.advance();
2192 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
2193 }
2194 "maxvalue" => {
2195 self.advance();
2196 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
2197 }
2198 "no" => {
2199 self.advance();
2200 let what = self.expect_ident_like()?;
2201 match what.to_ascii_lowercase().as_str() {
2202 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
2203 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
2204 "cycle" => opts.cycle = Some(false),
2205 other => {
2206 return Err(self.err(alloc::format!(
2207 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
2208 )));
2209 }
2210 }
2211 }
2212 "start" => {
2213 self.advance();
2214 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2216 if s.eq_ignore_ascii_case("with"))
2217 {
2218 self.advance();
2219 }
2220 opts.start = Some(self.expect_signed_int()?);
2221 }
2222 "restart" if allow_restart => {
2223 self.advance();
2224 let mut with_val: Option<i64> = None;
2226 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2227 if s.eq_ignore_ascii_case("with"))
2228 {
2229 self.advance();
2230 with_val = Some(self.expect_signed_int()?);
2231 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
2232 with_val = Some(self.expect_signed_int()?);
2233 }
2234 opts.restart = Some(with_val);
2235 }
2236 "cache" => {
2237 self.advance();
2238 opts.cache = Some(self.expect_signed_int()?);
2239 }
2240 "cycle" => {
2241 self.advance();
2242 opts.cycle = Some(true);
2243 }
2244 "owned" => {
2245 self.advance();
2246 match self.peek() {
2248 Token::By => {
2249 self.advance();
2250 }
2251 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
2252 self.advance();
2253 }
2254 other => {
2255 return Err(
2256 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
2257 );
2258 }
2259 }
2260 let first = match self.advance() {
2264 Token::Ident(s) | Token::QuotedIdent(s) => s,
2265 other => {
2266 return Err(self.err(alloc::format!(
2267 "expected identifier or NONE after OWNED BY, got {other:?}"
2268 )));
2269 }
2270 };
2271 if first.eq_ignore_ascii_case("none") {
2272 opts.owned_by = Some(SequenceOwnedBy::None);
2273 } else if matches!(self.peek(), Token::Dot) {
2274 self.advance();
2275 let second = match self.advance() {
2276 Token::Ident(s) | Token::QuotedIdent(s) => s,
2277 other => {
2278 return Err(self.err(alloc::format!(
2279 "expected column name after OWNED BY {first}., got {other:?}"
2280 )));
2281 }
2282 };
2283 if matches!(self.peek(), Token::Dot) {
2292 self.advance();
2293 let third = match self.advance() {
2294 Token::Ident(s) | Token::QuotedIdent(s) => s,
2295 other => {
2296 return Err(self.err(alloc::format!(
2297 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2298 )));
2299 }
2300 };
2301 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2303 table: second,
2304 column: third,
2305 });
2306 } else {
2307 opts.owned_by = Some(SequenceOwnedBy::Column {
2308 table: first,
2309 column: second,
2310 });
2311 }
2312 } else {
2313 return Err(self.err(alloc::format!(
2314 "expected table.column or NONE after OWNED BY, got {first:?}"
2315 )));
2316 }
2317 }
2318 _ => break,
2319 }
2320 }
2321 Ok(opts)
2322 }
2323
2324 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2325 let neg = if matches!(self.peek(), Token::Minus) {
2326 self.advance();
2327 true
2328 } else {
2329 false
2330 };
2331 match self.peek() {
2332 Token::Integer(n) => {
2333 let v = *n;
2334 self.advance();
2335 Ok(if neg { -v } else { v })
2336 }
2337 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2338 }
2339 }
2340
2341 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2351 loop {
2352 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2354 self.advance();
2355 self.consume_optional_initially_clause()?;
2356 continue;
2357 }
2358 if matches!(self.peek(), Token::Not) {
2360 let look = self.tokens.get(self.pos + 1);
2361 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2362 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2365 continue;
2366 }
2367 break;
2368 }
2369 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2374 self.consume_optional_initially_clause()?;
2375 continue;
2376 }
2377 break;
2378 }
2379 Ok(())
2380 }
2381
2382 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2386 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2387 return Ok(());
2388 }
2389 self.advance(); match self.advance() {
2391 Token::Ident(s)
2392 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2393 {
2394 Ok(())
2395 }
2396 other => Err(self.err(alloc::format!(
2397 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2398 ))),
2399 }
2400 }
2401
2402 fn consume_mysql_routine_body(&mut self) {
2420 let mut depth: i32 = 0;
2425 let mut started = false;
2426 loop {
2427 match self.peek().clone() {
2428 Token::Begin => {
2429 self.advance();
2430 depth += 1;
2431 started = true;
2432 }
2433 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2434 self.advance();
2435 if started {
2436 depth -= 1;
2437 if depth <= 0 {
2438 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2445 let is_inner_close = matches!(
2450 self.peek(),
2451 Token::Ident(s) | Token::QuotedIdent(s)
2452 if matches!(
2453 s.to_ascii_lowercase().as_str(),
2454 "if" | "loop" | "while" | "case" | "repeat"
2455 )
2456 );
2457 if is_inner_close {
2458 self.advance();
2459 depth += 1;
2460 continue;
2461 }
2462 }
2463 if matches!(self.peek(), Token::Semicolon) {
2465 self.advance();
2466 }
2467 return;
2468 }
2469 }
2470 }
2471 Token::Eof => return,
2472 _ => {
2473 self.advance();
2474 }
2475 }
2476 }
2477 }
2478
2479 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2493 loop {
2494 match self.peek().clone() {
2495 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2496 self.advance(); if matches!(self.peek(), Token::Eq) {
2500 self.advance();
2501 }
2502 if matches!(
2506 self.peek(),
2507 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2508 ) {
2509 self.advance();
2510 }
2511 }
2512 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2513 self.advance(); if matches!(self.peek(), Token::Eq) {
2515 self.advance();
2516 }
2517 match self.peek().clone() {
2520 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2521 self.advance();
2522 if matches!(self.peek(), Token::At) {
2524 self.advance();
2525 if matches!(
2526 self.peek(),
2527 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2528 ) {
2529 self.advance();
2530 }
2531 }
2532 }
2533 _ => {}
2534 }
2535 }
2536 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2537 let save = self.pos;
2542 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2544 if s2.eq_ignore_ascii_case("security"))
2545 {
2546 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2549 self.advance();
2550 }
2551 } else {
2552 self.pos = save;
2555 return Ok(());
2556 }
2557 }
2558 _ => return Ok(()),
2559 }
2560 }
2561 }
2562
2563 fn parse_if_not_exists(&mut self) -> bool {
2564 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2565 {
2566 let save = self.pos;
2567 self.advance();
2568 if matches!(self.peek(), Token::Not) {
2569 self.advance();
2570 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2571 {
2572 self.advance();
2573 return true;
2574 }
2575 }
2576 self.pos = save;
2577 }
2578 false
2579 }
2580
2581 fn parse_if_exists(&mut self) -> bool {
2582 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2583 {
2584 let save = self.pos;
2585 self.advance();
2586 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2587 {
2588 self.advance();
2589 return true;
2590 }
2591 self.pos = save;
2592 }
2593 false
2594 }
2595
2596 fn parse_create_trigger_after_keyword(
2600 &mut self,
2601 or_replace: bool,
2602 ) -> Result<Statement, ParseError> {
2603 let name = self.expect_ident_like()?;
2604 let timing = {
2605 let ident = self.expect_ident_like()?;
2606 if ident.eq_ignore_ascii_case("before") {
2607 TriggerTiming::Before
2608 } else if ident.eq_ignore_ascii_case("after") {
2609 TriggerTiming::After
2610 } else if ident.eq_ignore_ascii_case("instead") {
2611 let next = self.expect_ident_like()?;
2612 if !next.eq_ignore_ascii_case("of") {
2613 return Err(self.err(alloc::format!(
2614 "expected OF after INSTEAD in trigger timing, got {next:?}"
2615 )));
2616 }
2617 TriggerTiming::InsteadOf
2618 } else {
2619 return Err(self.err(alloc::format!(
2620 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2621 )));
2622 }
2623 };
2624 let mut events: Vec<TriggerEvent> = Vec::new();
2631 let mut update_columns: Vec<String> = Vec::new();
2632 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2633 events.push(first_ev);
2634 if !first_cols.is_empty() {
2635 update_columns = first_cols;
2636 }
2637 while matches!(self.peek(), Token::Or) {
2638 self.advance();
2639 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2640 events.push(ev);
2641 if !cols.is_empty() {
2642 if !update_columns.is_empty() {
2643 return Err(
2644 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2645 );
2646 }
2647 update_columns = cols;
2648 }
2649 }
2650 let tok = self.peek();
2652 let Token::On = tok else {
2653 return Err(self.err(alloc::format!(
2654 "expected ON after trigger events, got {tok:?}"
2655 )));
2656 };
2657 self.advance();
2658 let table = self.expect_ident_like()?;
2659 if !matches!(self.peek(), Token::For) {
2663 return Err(self.err(alloc::format!(
2664 "expected FOR EACH ROW / STATEMENT, got {:?}",
2665 self.peek()
2666 )));
2667 }
2668 self.advance();
2669 let for_each = {
2670 let e = self.expect_ident_like()?;
2671 if !e.eq_ignore_ascii_case("each") {
2672 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2673 }
2674 let unit = self.expect_ident_like()?;
2675 if unit.eq_ignore_ascii_case("row") {
2676 TriggerForEach::Row
2677 } else if unit.eq_ignore_ascii_case("statement") {
2678 TriggerForEach::Statement
2679 } else {
2680 return Err(self.err(alloc::format!(
2681 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2682 )));
2683 }
2684 };
2685 let exec = self.expect_ident_like()?;
2687 if !exec.eq_ignore_ascii_case("execute") {
2688 return Err(self.err(alloc::format!(
2689 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2690 )));
2691 }
2692 let fn_or_proc = self.expect_ident_like()?;
2693 if !(fn_or_proc.eq_ignore_ascii_case("function")
2694 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2695 {
2696 return Err(self.err(alloc::format!(
2697 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2698 )));
2699 }
2700 let function = self.expect_ident_like()?;
2701 if matches!(self.peek(), Token::LParen) {
2703 self.advance();
2704 if !matches!(self.peek(), Token::RParen) {
2705 return Err(self.err(alloc::format!(
2706 "v7.12.4 trigger function calls take no args; got {:?}",
2707 self.peek()
2708 )));
2709 }
2710 self.advance();
2711 }
2712 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2713 name,
2714 or_replace,
2715 timing,
2716 events,
2717 table,
2718 for_each,
2719 function,
2720 update_columns,
2721 }))
2722 }
2723
2724 fn parse_trigger_event_with_optional_of(
2728 &mut self,
2729 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2730 let ev = self.parse_trigger_event()?;
2731 if !matches!(ev, TriggerEvent::Update) {
2732 return Ok((ev, Vec::new()));
2733 }
2734 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2736 return Ok((ev, Vec::new()));
2737 }
2738 self.advance(); let mut cols: Vec<String> = Vec::new();
2740 loop {
2741 cols.push(self.expect_ident_like()?);
2742 if matches!(self.peek(), Token::Comma) {
2743 self.advance();
2744 continue;
2745 }
2746 break;
2747 }
2748 if cols.is_empty() {
2749 return Err(
2750 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2751 );
2752 }
2753 Ok((ev, cols))
2754 }
2755
2756 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2763 let declarations = if matches!(
2765 self.peek(),
2766 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2767 ) {
2768 self.advance();
2769 self.parse_plpgsql_declare_block()?
2770 } else {
2771 Vec::new()
2772 };
2773 if !matches!(self.peek(), Token::Begin) {
2778 return Err(self.err(alloc::format!(
2779 "expected BEGIN at start of plpgsql block, got {:?}",
2780 self.peek()
2781 )));
2782 }
2783 self.advance();
2784 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2785 Ok(PlPgSqlBlock {
2786 declarations,
2787 statements,
2788 })
2789 }
2790
2791 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2795 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2796 loop {
2797 if matches!(self.peek(), Token::Begin) {
2798 return Ok(out);
2799 }
2800 let name = self.expect_ident_like()?;
2801 let ty_token = self.expect_ident_like()?;
2802 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2803 Some(t) => FunctionArgType::Typed(t),
2804 None => FunctionArgType::Raw(ty_token),
2805 };
2806 let default = match self.peek() {
2807 Token::ColonEq => {
2808 self.advance();
2809 Some(self.parse_expr(0)?)
2810 }
2811 Token::Eq => {
2812 self.advance();
2816 Some(self.parse_expr(0)?)
2817 }
2818 _ => None,
2819 };
2820 if !matches!(self.peek(), Token::Semicolon) {
2822 return Err(self.err(alloc::format!(
2823 "expected ; after DECLARE entry for {name:?}, got {:?}",
2824 self.peek()
2825 )));
2826 }
2827 self.advance();
2828 out.push(PlPgSqlDeclare { name, ty, default });
2829 }
2830 }
2831
2832 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2837 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2838 loop {
2839 while matches!(self.peek(), Token::Semicolon) {
2841 self.advance();
2842 }
2843 if matches!(
2845 self.peek(),
2846 Token::Ident(s) | Token::QuotedIdent(s)
2847 if s.eq_ignore_ascii_case("end")
2848 || s.eq_ignore_ascii_case("else")
2849 || s.eq_ignore_ascii_case("elsif")
2850 || s.eq_ignore_ascii_case("elseif")
2851 ) {
2852 return Ok(statements);
2853 }
2854 let stmt = self.parse_plpgsql_stmt()?;
2857 statements.push(stmt);
2858 match self.peek() {
2859 Token::Semicolon => {
2860 self.advance();
2861 }
2862 Token::Ident(s) | Token::QuotedIdent(s)
2863 if s.eq_ignore_ascii_case("end")
2864 || s.eq_ignore_ascii_case("else")
2865 || s.eq_ignore_ascii_case("elsif")
2866 || s.eq_ignore_ascii_case("elseif") =>
2867 {
2868 }
2870 other => {
2871 return Err(self.err(alloc::format!(
2872 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2873 )));
2874 }
2875 }
2876 }
2877 }
2878
2879 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2880 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2882 {
2883 self.advance();
2884 return self.parse_plpgsql_return();
2885 }
2886 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2888 {
2889 self.advance();
2890 return self.parse_plpgsql_if();
2891 }
2892 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2894 {
2895 self.advance();
2896 return self.parse_plpgsql_raise();
2897 }
2898 if matches!(self.peek(), Token::Select)
2909 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2910 {
2911 return Ok(PlPgSqlStmt::SelectInto {
2912 var: var_name,
2913 body: Box::new(select_body),
2914 });
2915 }
2916 if matches!(self.peek(), Token::Insert)
2926 || matches!(self.peek(), Token::Select)
2927 || matches!(self.peek(), Token::Create)
2928 || matches!(self.peek(), Token::Drop)
2929 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2930 if s.eq_ignore_ascii_case("update")
2931 || s.eq_ignore_ascii_case("delete")
2932 || s.eq_ignore_ascii_case("alter"))
2933 {
2934 let stmt = self.parse_one_statement()?;
2935 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2936 }
2937 let target = self.parse_plpgsql_assign_target()?;
2940 match self.peek() {
2943 Token::ColonEq => {
2944 self.advance();
2945 }
2946 Token::Colon => {
2947 self.advance();
2948 if !matches!(self.peek(), Token::Eq) {
2949 return Err(self.err(alloc::format!(
2950 "expected := after plpgsql assign target, got `:` then {:?}",
2951 self.peek()
2952 )));
2953 }
2954 self.advance();
2955 }
2956 other => {
2957 return Err(self.err(alloc::format!(
2958 "expected := after plpgsql assign target, got {other:?}"
2959 )));
2960 }
2961 }
2962 let value = self.parse_expr(0)?;
2963 Ok(PlPgSqlStmt::Assign { target, value })
2964 }
2965
2966 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2969 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
2970 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
2971 loop {
2972 let cond = self.parse_expr(0)?;
2974 let then_kw = self.expect_ident_like()?;
2975 if !then_kw.eq_ignore_ascii_case("then") {
2976 return Err(self.err(alloc::format!(
2977 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
2978 )));
2979 }
2980 let body = self.parse_plpgsql_stmt_list_until_end()?;
2981 branches.push((cond, body));
2982 match self.peek() {
2984 Token::Ident(s) | Token::QuotedIdent(s)
2985 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
2986 {
2987 self.advance();
2988 continue;
2989 }
2990 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
2991 self.advance();
2992 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
2993 break;
2994 }
2995 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2996 break;
2997 }
2998 other => {
2999 return Err(self.err(alloc::format!(
3000 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
3001 )));
3002 }
3003 }
3004 }
3005 let end_kw = self.expect_ident_like()?;
3008 if !end_kw.eq_ignore_ascii_case("end") {
3009 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
3010 }
3011 let if_kw = self.expect_ident_like()?;
3012 if !if_kw.eq_ignore_ascii_case("if") {
3013 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
3014 }
3015 Ok(PlPgSqlStmt::If {
3016 branches,
3017 else_branch,
3018 })
3019 }
3020
3021 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3025 let lvl_ident = self.expect_ident_like()?;
3026 let level = match lvl_ident.to_ascii_lowercase().as_str() {
3027 "notice" => RaiseLevel::Notice,
3028 "warning" => RaiseLevel::Warning,
3029 "info" => RaiseLevel::Info,
3030 "log" => RaiseLevel::Log,
3031 "debug" => RaiseLevel::Debug,
3032 "exception" => RaiseLevel::Exception,
3033 other => {
3034 return Err(self.err(alloc::format!(
3035 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
3036 )));
3037 }
3038 };
3039 let Token::String(msg) = self.peek() else {
3043 return Err(self.err(alloc::format!(
3044 "expected RAISE message string, got {:?}",
3045 self.peek()
3046 )));
3047 };
3048 let message = msg.clone();
3049 self.advance();
3050 let mut args: Vec<Expr> = Vec::new();
3052 while matches!(self.peek(), Token::Comma) {
3053 self.advance();
3054 args.push(self.parse_expr(0)?);
3055 }
3056 Ok(PlPgSqlStmt::Raise {
3057 level,
3058 message,
3059 args,
3060 })
3061 }
3062
3063 #[allow(clippy::too_many_lines)]
3071 fn try_parse_plpgsql_select_into(
3072 &mut self,
3073 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
3074 let start = self.pos;
3079 let mut into_pos: Option<usize> = None;
3080 let mut depth: i32 = 0;
3081 let mut i = start + 1;
3082 while i < self.tokens.len() {
3083 match &self.tokens[i] {
3084 Token::LParen => depth += 1,
3085 Token::RParen => depth -= 1,
3086 Token::Semicolon if depth == 0 => break,
3087 Token::Ident(s)
3088 if depth == 0
3089 && (s.eq_ignore_ascii_case("end")
3090 || s.eq_ignore_ascii_case("else")
3091 || s.eq_ignore_ascii_case("elsif")) =>
3092 {
3093 break;
3094 }
3095 Token::Into if depth == 0 => {
3096 into_pos = Some(i);
3097 break;
3098 }
3099 _ => {}
3100 }
3101 i += 1;
3102 }
3103 let Some(into_at) = into_pos else {
3104 return Ok(None);
3105 };
3106 let var = match self.tokens.get(into_at + 1) {
3110 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
3111 other => {
3112 return Err(self.err(alloc::format!(
3113 "expected variable name after SELECT … INTO, got {other:?}"
3114 )));
3115 }
3116 };
3117 let mut end = into_at + 2;
3120 let mut depth2: i32 = 0;
3121 while end < self.tokens.len() {
3122 match &self.tokens[end] {
3123 Token::LParen => depth2 += 1,
3124 Token::RParen => depth2 -= 1,
3125 Token::Semicolon if depth2 == 0 => break,
3126 Token::Ident(s)
3127 if depth2 == 0
3128 && (s.eq_ignore_ascii_case("end")
3129 || s.eq_ignore_ascii_case("else")
3130 || s.eq_ignore_ascii_case("elsif")) =>
3131 {
3132 break;
3133 }
3134 _ => {}
3135 }
3136 end += 1;
3137 }
3138 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
3143 for j in start..into_at {
3144 rebuilt.push(self.tokens[j].clone());
3145 }
3146 for j in (into_at + 2)..end {
3147 rebuilt.push(self.tokens[j].clone());
3148 }
3149 rebuilt.push(Token::Eof);
3150 let saved_pos = self.pos;
3151 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
3152 self.pos = 0;
3153 if !matches!(self.peek(), Token::Select) {
3155 self.tokens = saved_tokens;
3156 self.pos = saved_pos;
3157 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
3158 }
3159 let sel = self.parse_select_stmt();
3160 self.tokens = saved_tokens;
3161 self.pos = end;
3162 let sel = sel?;
3163 let Statement::Select(body) = sel else {
3164 return Err(self.err(alloc::format!(
3165 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
3166 )));
3167 };
3168 Ok(Some((body, var)))
3169 }
3170
3171 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
3172 let head = match self.advance() {
3186 Token::Ident(s) | Token::QuotedIdent(s) => s,
3187 other => {
3188 return Err(self.err(alloc::format!(
3189 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
3190 )));
3191 }
3192 };
3193 if matches!(self.peek(), Token::Dot) {
3194 self.advance();
3195 let col = self.expect_ident_like()?;
3196 if head.eq_ignore_ascii_case("new") {
3197 return Ok(AssignTarget::NewColumn(col));
3198 }
3199 if head.eq_ignore_ascii_case("old") {
3200 return Ok(AssignTarget::OldColumn(col));
3201 }
3202 return Err(self.err(alloc::format!(
3203 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
3204 got {head:?}.<col>"
3205 )));
3206 }
3207 Ok(AssignTarget::Local(head))
3208 }
3209
3210 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3211 match self.peek() {
3213 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
3214 self.advance();
3215 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
3216 }
3217 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
3218 self.advance();
3219 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
3220 }
3221 Token::Null => {
3222 self.advance();
3223 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3224 }
3225 Token::Semicolon => {
3228 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3229 }
3230 _ => {}
3231 }
3232 let e = self.parse_expr(0)?;
3234 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
3235 }
3236
3237 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
3238 if matches!(self.peek(), Token::Insert) {
3243 self.advance();
3244 return Ok(TriggerEvent::Insert);
3245 }
3246 match self.peek() {
3247 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3248 self.advance();
3249 Ok(TriggerEvent::Update)
3250 }
3251 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3252 self.advance();
3253 Ok(TriggerEvent::Delete)
3254 }
3255 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
3256 self.advance();
3257 Ok(TriggerEvent::Truncate)
3258 }
3259 other => Err(self.err(alloc::format!(
3260 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3261 ))),
3262 }
3263 }
3264
3265 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3272 let name = self.expect_ident_or_string()?;
3273 let scope = if matches!(self.peek(), Token::For) {
3276 self.advance();
3277 if matches!(self.peek(), Token::All) {
3278 self.advance();
3279 if !matches!(self.peek(), Token::Tables) {
3280 return Err(self.err(format!(
3281 "expected TABLES after FOR ALL, got {:?}",
3282 self.peek()
3283 )));
3284 }
3285 self.advance();
3286 if matches!(self.peek(), Token::Except) {
3287 self.advance();
3288 let tables = self.parse_publication_table_list()?;
3289 PublicationScope::AllTablesExcept(tables)
3290 } else {
3291 PublicationScope::AllTables
3292 }
3293 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3294 self.advance();
3297 let tables = self.parse_publication_table_list()?;
3298 PublicationScope::ForTables(tables)
3299 } else {
3300 return Err(self.err(format!(
3301 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3302 self.peek()
3303 )));
3304 }
3305 } else {
3306 PublicationScope::AllTables
3307 };
3308 Ok(Statement::CreatePublication(CreatePublicationStatement {
3309 name,
3310 scope,
3311 }))
3312 }
3313
3314 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3319 let first = self.expect_ident_like()?;
3320 let mut out = alloc::vec![first];
3321 while matches!(self.peek(), Token::Comma) {
3322 self.advance();
3323 out.push(self.expect_ident_like()?);
3324 }
3325 Ok(out)
3326 }
3327
3328 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3336 let name = self.expect_ident_or_string()?;
3337 if !matches!(self.peek(), Token::Connection) {
3338 return Err(self.err(format!(
3339 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3340 self.peek()
3341 )));
3342 }
3343 self.advance();
3344 let conn_str = self.expect_string_literal()?;
3345 if !matches!(self.peek(), Token::Publication) {
3346 return Err(self.err(format!(
3347 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3348 self.peek()
3349 )));
3350 }
3351 self.advance();
3352 let first = self.expect_ident_like()?;
3355 let mut publications = alloc::vec![first];
3356 while matches!(self.peek(), Token::Comma) {
3357 self.advance();
3358 publications.push(self.expect_ident_like()?);
3359 }
3360 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3361 name,
3362 conn_str,
3363 publications,
3364 }))
3365 }
3366
3367 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3374 let mut name = self.expect_ident_like()?;
3375 while matches!(self.peek(), Token::Dot) {
3376 self.advance();
3377 let next = self.expect_ident_like()?;
3378 name.push('.');
3379 name.push_str(&next);
3380 }
3381 Ok(name.to_ascii_lowercase())
3382 }
3383
3384 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3385 match self.advance() {
3386 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3387 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3388 Ok(crate::ast::SetValue::Default)
3389 }
3390 Token::Ident(s) | Token::QuotedIdent(s) => {
3391 let mut accum = s;
3392 while matches!(self.peek(), Token::Dot) {
3393 self.advance();
3394 let next = self.expect_ident_like()?;
3395 accum.push('.');
3396 accum.push_str(&next);
3397 }
3398 Ok(crate::ast::SetValue::Ident(accum))
3399 }
3400 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3401 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3402 Token::On => Ok(crate::ast::SetValue::Ident("on".to_string())),
3407 Token::True => Ok(crate::ast::SetValue::Ident("true".to_string())),
3408 Token::False => Ok(crate::ast::SetValue::Ident("false".to_string())),
3409 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3415 Token::Minus => match self.advance() {
3420 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3421 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3422 other => Err(self.err(format!(
3423 "expected numeric after `-` in SET value, got {other:?}"
3424 ))),
3425 },
3426 other => Err(self.err(format!(
3427 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3428 ))),
3429 }
3430 }
3431
3432 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3433 if !matches!(self.peek(), Token::For) {
3437 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3438 }
3439 self.advance();
3440 self.expect_keyword_ident("wal")?;
3441 self.expect_keyword_ident("position")?;
3442 let pos = self.expect_u64_literal()?;
3443 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3444 {
3445 self.advance();
3446 self.expect_keyword_ident("timeout")?;
3447 Some(self.expect_u64_literal()?)
3448 } else {
3449 None
3450 };
3451 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3452 }
3453
3454 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3458 match self.advance() {
3459 Token::Integer(n) if n >= 0 => Ok(n as u64),
3460 Token::Integer(n) => Err(ParseError {
3461 message: format!("expected non-negative integer, got {n}"),
3462 token_pos: self.pos.saturating_sub(1),
3463 }),
3464 other => Err(ParseError {
3465 message: format!("expected integer literal, got {other:?}"),
3466 token_pos: self.pos.saturating_sub(1),
3467 }),
3468 }
3469 }
3470
3471 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3475 let name = self.expect_ident_or_string()?;
3476 self.expect_keyword_ident("with")?;
3477 self.expect_keyword_ident("password")?;
3478 let password = self.expect_string_literal()?;
3479 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3480 && s.eq_ignore_ascii_case("role")
3481 {
3482 self.advance();
3483 self.expect_string_literal()?
3484 } else {
3485 "readonly".to_string()
3486 };
3487 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3488 name,
3489 password,
3490 role,
3491 }))
3492 }
3493
3494 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3497 let table = self.expect_ident_like()?;
3498 self.expect_keyword_ident("set")?;
3499 let mut assignments = Vec::new();
3500 loop {
3501 let col = self.expect_ident_like()?;
3502 if !matches!(self.peek(), Token::Eq) {
3503 return Err(self.err(format!(
3504 "expected `=` after column name in UPDATE SET, got {:?}",
3505 self.peek()
3506 )));
3507 }
3508 self.advance();
3509 let value = self.parse_expr(0)?;
3510 assignments.push((col, value));
3511 if matches!(self.peek(), Token::Comma) {
3512 self.advance();
3513 continue;
3514 }
3515 break;
3516 }
3517 let where_ = if matches!(self.peek(), Token::Where) {
3518 self.advance();
3519 Some(self.parse_expr(0)?)
3520 } else {
3521 None
3522 };
3523 let returning = self.parse_optional_returning()?;
3524 Ok(Statement::Update(crate::ast::UpdateStatement {
3525 ctes: Vec::new(),
3526 table,
3527 assignments,
3528 where_,
3529 returning,
3530 }))
3531 }
3532
3533 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3536 if !matches!(self.peek(), Token::From) {
3537 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3538 }
3539 self.advance();
3540 let table = self.expect_ident_like()?;
3541 let where_ = if matches!(self.peek(), Token::Where) {
3542 self.advance();
3543 Some(self.parse_expr(0)?)
3544 } else {
3545 None
3546 };
3547 let returning = self.parse_optional_returning()?;
3548 Ok(Statement::Delete(crate::ast::DeleteStatement {
3549 ctes: Vec::new(),
3550 table,
3551 where_,
3552 returning,
3553 }))
3554 }
3555
3556 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3566 let is_into_kw = matches!(self.peek(), Token::Into)
3568 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3569 if !is_into_kw {
3570 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3571 }
3572 self.advance();
3573 let target = self.expect_ident_like()?;
3574 let target_alias = match self.peek() {
3576 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3577 Some(self.expect_ident_like()?)
3578 }
3579 _ => None,
3580 };
3581 let is_using_kw = matches!(
3583 self.peek(),
3584 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3585 );
3586 if !is_using_kw {
3587 return Err(self.err(format!(
3588 "expected USING after MERGE INTO target, got {:?}",
3589 self.peek()
3590 )));
3591 }
3592 self.advance();
3593 let source = self.expect_ident_like()?;
3594 let source_alias = match self.peek() {
3595 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3596 Some(self.expect_ident_like()?)
3597 }
3598 _ => None,
3599 };
3600 if !matches!(self.peek(), Token::On) {
3602 return Err(self.err(format!(
3603 "expected ON after MERGE … USING source, got {:?}",
3604 self.peek()
3605 )));
3606 }
3607 self.advance();
3608 let on = self.parse_expr(0)?;
3609 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3611 loop {
3612 let is_when_kw = matches!(
3613 self.peek(),
3614 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3615 );
3616 if !is_when_kw {
3617 break;
3618 }
3619 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3622 self.advance();
3623 crate::ast::MergeMatched::NotMatched
3624 } else {
3625 crate::ast::MergeMatched::Matched
3626 };
3627 let is_matched_kw = matches!(
3628 self.peek(),
3629 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3630 );
3631 if !is_matched_kw {
3632 return Err(self.err(format!(
3633 "expected MATCHED in WHEN clause, got {:?}",
3634 self.peek()
3635 )));
3636 }
3637 self.advance();
3638 let condition = if matches!(self.peek(), Token::And) {
3640 self.advance();
3641 Some(self.parse_expr(0)?)
3642 } else {
3643 None
3644 };
3645 let is_then_kw = matches!(
3647 self.peek(),
3648 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3649 );
3650 if !is_then_kw {
3651 return Err(self.err(format!(
3652 "expected THEN in WHEN clause, got {:?}",
3653 self.peek()
3654 )));
3655 }
3656 self.advance();
3657 let action = match self.peek().clone() {
3659 Token::Insert => {
3660 self.advance();
3661 if !matches!(self.peek(), Token::LParen) {
3663 return Err(self.err(format!(
3664 "expected '(' after INSERT in MERGE, got {:?}",
3665 self.peek()
3666 )));
3667 }
3668 self.advance();
3669 let mut columns: Vec<String> = Vec::new();
3670 loop {
3671 columns.push(self.expect_ident_like()?);
3672 if matches!(self.peek(), Token::Comma) {
3673 self.advance();
3674 continue;
3675 }
3676 break;
3677 }
3678 if !matches!(self.peek(), Token::RParen) {
3679 return Err(self.err(format!(
3680 "expected ')' after INSERT column list, got {:?}",
3681 self.peek()
3682 )));
3683 }
3684 self.advance();
3685 if !matches!(self.peek(), Token::Values) {
3687 return Err(self.err(format!(
3688 "expected VALUES in MERGE INSERT, got {:?}",
3689 self.peek()
3690 )));
3691 }
3692 self.advance();
3693 if !matches!(self.peek(), Token::LParen) {
3694 return Err(self.err(format!(
3695 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3696 self.peek()
3697 )));
3698 }
3699 self.advance();
3700 let mut values: Vec<crate::ast::Expr> = Vec::new();
3701 loop {
3702 values.push(self.parse_expr(0)?);
3703 if matches!(self.peek(), Token::Comma) {
3704 self.advance();
3705 continue;
3706 }
3707 break;
3708 }
3709 if !matches!(self.peek(), Token::RParen) {
3710 return Err(self.err(format!(
3711 "expected ')' after MERGE INSERT values, got {:?}",
3712 self.peek()
3713 )));
3714 }
3715 self.advance();
3716 if columns.len() != values.len() {
3717 return Err(self.err(format!(
3718 "MERGE INSERT column count ({}) ≠ value count ({})",
3719 columns.len(),
3720 values.len()
3721 )));
3722 }
3723 crate::ast::MergeAction::Insert { columns, values }
3724 }
3725 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3726 self.advance();
3727 let is_set_kw = matches!(
3729 self.peek(),
3730 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3731 );
3732 if !is_set_kw {
3733 return Err(self.err(format!(
3734 "expected SET after UPDATE in MERGE, got {:?}",
3735 self.peek()
3736 )));
3737 }
3738 self.advance();
3739 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3740 loop {
3741 let col = self.expect_ident_like()?;
3742 if !matches!(self.peek(), Token::Eq) {
3743 return Err(self.err(format!(
3744 "expected '=' in MERGE UPDATE assignment, got {:?}",
3745 self.peek()
3746 )));
3747 }
3748 self.advance();
3749 let expr = self.parse_expr(0)?;
3750 assignments.push((col, expr));
3751 if matches!(self.peek(), Token::Comma) {
3752 self.advance();
3753 continue;
3754 }
3755 break;
3756 }
3757 crate::ast::MergeAction::Update { assignments }
3758 }
3759 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3760 self.advance();
3761 crate::ast::MergeAction::Delete
3762 }
3763 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3764 self.advance();
3765 let is_nothing_kw = matches!(
3766 self.peek(),
3767 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3768 );
3769 if !is_nothing_kw {
3770 return Err(self.err(format!(
3771 "expected NOTHING after DO in MERGE clause, got {:?}",
3772 self.peek()
3773 )));
3774 }
3775 self.advance();
3776 crate::ast::MergeAction::DoNothing
3777 }
3778 other => {
3779 return Err(self.err(format!(
3780 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3781 )));
3782 }
3783 };
3784 clauses.push(crate::ast::MergeWhenClause {
3785 matched,
3786 condition,
3787 action,
3788 });
3789 }
3790 if clauses.is_empty() {
3791 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3792 }
3793 Ok(Statement::Merge(crate::ast::MergeStatement {
3794 target,
3795 target_alias,
3796 source,
3797 source_alias,
3798 on,
3799 clauses,
3800 }))
3801 }
3802
3803 fn parse_optional_returning(
3808 &mut self,
3809 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3810 let is_returning_kw = matches!(
3811 self.peek(),
3812 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3813 );
3814 if !is_returning_kw {
3815 return Ok(None);
3816 }
3817 self.advance();
3818 let mut items = Vec::new();
3819 loop {
3820 items.push(self.parse_select_item()?);
3821 if matches!(self.peek(), Token::Comma) {
3822 self.advance();
3823 continue;
3824 }
3825 break;
3826 }
3827 Ok(Some(items))
3828 }
3829
3830 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
3838 match self.advance() {
3845 Token::Index => {}
3846 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
3847 Token::Table => {
3850 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3851 self.advance();
3852 }
3853 return self.parse_alter_table_after_keyword();
3854 }
3855 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
3856 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3857 self.advance();
3858 }
3859 return self.parse_alter_table_after_keyword();
3860 }
3861 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
3864 return self.parse_alter_sequence_after_keyword();
3865 }
3866 Token::Ident(s) | Token::QuotedIdent(s)
3872 if matches!(
3873 s.to_ascii_lowercase().as_str(),
3874 "view"
3875 | "function"
3876 | "type"
3877 | "domain"
3878 | "database"
3879 | "role"
3880 | "schema"
3881 | "owner"
3882 | "default"
3883 | "extension"
3884 | "materialized"
3885 | "policy"
3886 | "publication"
3887 | "subscription"
3888 ) =>
3889 {
3890 self.consume_until_statement_boundary();
3891 return Ok(Statement::Empty);
3892 }
3893 other => {
3894 return Err(self.err(format!(
3895 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
3896 after ALTER, got {other:?}"
3897 )));
3898 }
3899 }
3900 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3905 let next = self.tokens.get(self.pos + 1);
3906 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3907 self.advance();
3908 self.advance();
3909 true
3910 } else {
3911 false
3912 }
3913 } else {
3914 false
3915 };
3916 let name = self.expect_ident_like()?;
3917 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
3921 self.advance();
3922 if matches!(self.peek(), Token::To) {
3923 self.advance();
3924 } else {
3925 self.expect_keyword_ident("to")?;
3926 }
3927 let new = self.expect_ident_like()?;
3928 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3929 name,
3930 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
3931 }));
3932 }
3933 self.expect_keyword_ident("rebuild")?;
3935 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3937 self.advance();
3938 if !matches!(self.peek(), Token::LParen) {
3939 return Err(self.err(format!(
3940 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
3941 self.peek()
3942 )));
3943 }
3944 self.advance();
3945 self.expect_keyword_ident("encoding")?;
3946 if !matches!(self.peek(), Token::Eq) {
3947 return Err(self.err(format!(
3948 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
3949 self.peek()
3950 )));
3951 }
3952 self.advance();
3953 let enc_ident = match self.advance() {
3954 Token::Ident(s) | Token::QuotedIdent(s) => s,
3955 other => {
3956 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
3957 }
3958 };
3959 let enc = match enc_ident.to_ascii_lowercase().as_str() {
3960 "f32" => VecEncoding::F32,
3961 "sq8" => VecEncoding::Sq8,
3962 "half" => VecEncoding::F16,
3963 other => {
3964 return Err(self.err(format!(
3965 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
3966 )));
3967 }
3968 };
3969 if !matches!(self.peek(), Token::RParen) {
3970 return Err(self.err(format!(
3971 "expected ')' after encoding value, got {:?}",
3972 self.peek()
3973 )));
3974 }
3975 self.advance();
3976 Some(enc)
3977 } else {
3978 None
3979 };
3980 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3981 name,
3982 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
3983 }))
3984 }
3985
3986 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
3992 let table_name = self.expect_ident_like()?;
3993 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
3994 loop {
3995 let subaction = self.parse_alter_table_subaction()?;
3996 targets.extend(subaction);
4000 if matches!(self.peek(), Token::Comma) {
4001 self.advance();
4002 continue;
4003 }
4004 break;
4005 }
4006 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
4007 name: table_name,
4008 targets,
4009 }))
4010 }
4011
4012 fn parse_alter_table_subaction(
4016 &mut self,
4017 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
4018 match self.peek() {
4019 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4020 self.advance();
4021 let setting = self.expect_ident_like()?;
4022 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
4023 return Err(self.err(alloc::format!(
4024 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
4025 )));
4026 }
4027 if !matches!(self.peek(), Token::Eq) {
4028 return Err(self.err(alloc::format!(
4029 "expected '=' after hot_tier_bytes, got {:?}",
4030 self.peek()
4031 )));
4032 }
4033 self.advance();
4034 let n = self.expect_u64_literal()?;
4035 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
4036 }
4037 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4038 self.advance();
4039 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
4046 {
4047 let kind_pos = self.pos + 2;
4050 let kind = self.tokens.get(kind_pos).cloned();
4051 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
4052 {
4053 let fk = self.parse_table_level_fk()?;
4054 return Ok(alloc::vec![
4055 crate::ast::AlterTableTarget::AddForeignKey(fk)
4056 ]);
4057 }
4058 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
4059 {
4060 self.advance(); let _name = self.expect_ident_like()?;
4062 self.advance(); self.expect_keyword_ident("key")?;
4064 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4065 return Ok(alloc::vec![
4066 crate::ast::AlterTableTarget::AddTableConstraint(
4067 crate::ast::TableConstraint::PrimaryKey {
4068 name: None,
4069 columns: cols,
4070 }
4071 )
4072 ]);
4073 }
4074 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
4075 {
4076 self.advance(); let _name = self.expect_ident_like()?;
4078 let uc = self.parse_table_level_unique()?;
4084 return Ok(alloc::vec![
4085 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4086 ]);
4087 }
4088 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
4089 {
4090 self.advance(); let _name = self.expect_ident_like()?;
4092 self.advance(); if !matches!(self.peek(), Token::LParen) {
4094 return Err(self.err(alloc::format!(
4095 "expected '(' after CHECK, got {:?}", self.peek()
4096 )));
4097 }
4098 self.advance();
4099 let expr = self.parse_expr(0)?;
4100 if matches!(self.peek(), Token::RParen) {
4101 self.advance();
4102 }
4103 return Ok(alloc::vec![
4104 crate::ast::AlterTableTarget::AddTableConstraint(
4105 crate::ast::TableConstraint::Check { name: None, expr }
4106 )
4107 ]);
4108 }
4109 }
4112 let is_fk = matches!(
4113 self.peek(),
4114 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
4115 || s.eq_ignore_ascii_case("foreign")
4116 );
4117 if is_fk {
4118 let fk = self.parse_table_level_fk()?;
4119 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
4120 }
4121 match self.peek().clone() {
4124 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
4125 self.advance();
4126 self.expect_keyword_ident("key")?;
4127 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4128 return Ok(alloc::vec![
4129 crate::ast::AlterTableTarget::AddTableConstraint(
4130 crate::ast::TableConstraint::PrimaryKey {
4131 name: None,
4132 columns: cols,
4133 }
4134 )
4135 ]);
4136 }
4137 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4138 let uc = self.parse_table_level_unique()?;
4140 return Ok(alloc::vec![
4141 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4142 ]);
4143 }
4144 _ => {}
4145 }
4146 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4147 self.advance();
4148 }
4149 let mut if_not_exists = false;
4150 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4151 self.advance();
4152 if !matches!(self.peek(), Token::Not) {
4153 return Err(self.err(alloc::format!(
4154 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
4155 self.peek()
4156 )));
4157 }
4158 self.advance();
4159 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
4160 return Err(self.err(alloc::format!(
4161 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
4162 self.peek()
4163 )));
4164 }
4165 self.advance();
4166 if_not_exists = true;
4167 }
4168 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
4172 let col_name = column.name.clone();
4173 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
4174 column,
4175 if_not_exists,
4176 }];
4177 if let Some(mut fk) = col_level_fk {
4178 if fk.columns.is_empty() {
4179 fk.columns.push(col_name);
4180 }
4181 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
4182 }
4183 Ok(out)
4184 }
4185 Token::Drop => {
4186 self.advance();
4187 let subject = match self.peek() {
4194 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
4195 self.advance();
4196 "constraint"
4197 }
4198 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
4199 self.advance();
4200 "column"
4201 }
4202 Token::Ident(_) | Token::QuotedIdent(_) => "column",
4206 other => {
4207 return Err(self.err(alloc::format!(
4208 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
4209 )));
4210 }
4211 };
4212 let mut if_exists = false;
4213 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4214 let n1 = self.tokens.get(self.pos + 1);
4215 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4216 self.advance();
4217 self.advance();
4218 if_exists = true;
4219 }
4220 }
4221 let name = self.expect_ident_like()?;
4222 let mut cascade = false;
4223 if matches!(
4224 self.peek(),
4225 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
4226 || s.eq_ignore_ascii_case("restrict")
4227 ) {
4228 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
4229 {
4230 cascade = true;
4231 }
4232 self.advance();
4233 }
4234 if subject == "constraint" {
4235 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
4236 name,
4237 if_exists,
4238 }])
4239 } else {
4240 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
4241 column: name,
4242 if_exists,
4243 cascade,
4244 }])
4245 }
4246 }
4247 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
4248 self.advance();
4249 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4250 self.advance();
4251 }
4252 let col_name = self.expect_ident_like()?;
4253 match self.peek() {
4254 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
4255 self.advance();
4256 }
4257 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4265 let is_default_nextval =
4276 matches!(self.tokens.get(self.pos + 1), Some(Token::Default))
4277 && matches!(
4278 self.tokens.get(self.pos + 2),
4279 Some(Token::Ident(f)) if f.eq_ignore_ascii_case("nextval")
4280 );
4281 let seq_name = if is_default_nextval {
4284 self.scan_sequence_name_until_boundary()
4285 } else {
4286 self.consume_until_statement_boundary();
4287 None
4288 };
4289 if is_default_nextval {
4290 return Ok(alloc::vec![
4291 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4292 column: col_name,
4293 seq_name,
4294 }
4295 ]);
4296 }
4297 return Ok(Vec::new());
4303 }
4304 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4305 self.consume_until_statement_boundary();
4307 return Ok(Vec::new());
4308 }
4309 Token::Drop => {
4310 self.consume_until_statement_boundary();
4316 return Ok(Vec::new());
4317 }
4318 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4319 let is_generated = matches!(
4327 self.tokens.get(self.pos + 1),
4328 Some(Token::Ident(g)) if g.eq_ignore_ascii_case("generated")
4329 );
4330 if !is_generated {
4331 return Err(self.err(alloc::format!(
4332 "expected GENERATED after ALTER COLUMN {col_name} ADD, got {:?}",
4333 self.tokens.get(self.pos + 1)
4334 )));
4335 }
4336 let seq_name = self.scan_sequence_name_until_boundary();
4337 return Ok(alloc::vec![
4338 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4339 column: col_name,
4340 seq_name,
4341 }
4342 ]);
4343 }
4344 other => {
4345 return Err(self.err(alloc::format!(
4346 "expected TYPE / SET / DROP / ADD after ALTER COLUMN <name>, got {other:?}"
4347 )));
4348 }
4349 }
4350 let new_type = self.parse_column_type_name()?;
4351 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4352 {
4353 self.advance();
4354 Some(self.parse_expr(0)?)
4355 } else {
4356 None
4357 };
4358 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4359 column: col_name,
4360 new_type,
4361 using,
4362 }])
4363 }
4364 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4371 self.advance();
4372 if matches!(self.peek(), Token::To)
4377 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4378 {
4379 self.advance();
4380 let new = self.expect_ident_like()?;
4381 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4382 new,
4383 }]);
4384 }
4385 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4386 self.advance();
4387 }
4388 let old = self.expect_ident_like()?;
4389 if matches!(self.peek(), Token::To) {
4392 self.advance();
4393 } else {
4394 self.expect_keyword_ident("to")?;
4395 }
4396 let new = self.expect_ident_like()?;
4397 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4398 old,
4399 new,
4400 }])
4401 }
4402 Token::Ident(s)
4409 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4410 {
4411 let enabled = s.eq_ignore_ascii_case("enable");
4412 self.advance();
4413 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("keys")) {
4423 self.advance();
4424 return Ok(Vec::new());
4425 }
4426 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4427 return Err(self.err(alloc::format!(
4428 "expected TRIGGER after {}, got {:?}",
4429 if enabled { "ENABLE" } else { "DISABLE" },
4430 self.peek()
4431 )));
4432 }
4433 self.advance();
4434 let which = if matches!(self.peek(), Token::All)
4437 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4438 {
4439 self.advance();
4440 crate::ast::TriggerSelector::All
4441 } else {
4442 let name = self.expect_ident_like()?;
4443 crate::ast::TriggerSelector::Named(name)
4444 };
4445 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4446 which,
4447 enabled,
4448 }])
4449 }
4450 other => Err(self.err(alloc::format!(
4451 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4452 ))),
4453 }
4454 }
4455
4456 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4465 let schema = match self.tokens.get(self.pos) {
4467 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4468 _ => return None,
4469 };
4470 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4472 return None;
4473 }
4474 let tbl = match self.tokens.get(self.pos + 2)? {
4478 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4479 Token::Tables => "tables".to_string(),
4480 _ => return None,
4483 };
4484 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4488 ("__spg_info_", tbl.to_ascii_lowercase())
4489 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4490 let bare = tbl
4491 .to_ascii_lowercase()
4492 .strip_prefix("pg_")
4493 .map(alloc::string::String::from)
4494 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4495 ("__spg_pg_", bare)
4496 } else if schema.eq_ignore_ascii_case("mysql") {
4497 ("__spg_mysql_", tbl.to_ascii_lowercase())
4501 } else {
4502 return None;
4503 };
4504 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4508 }
4509
4510 fn try_peek_meta_bare(&mut self) -> Option<String> {
4517 const PG_META_TABLES: &[&str] = &[
4518 "pg_attribute",
4519 "pg_class",
4520 "pg_constraint",
4521 "pg_database",
4522 "pg_extension",
4523 "pg_index",
4524 "pg_indexes",
4525 "pg_matviews",
4526 "pg_namespace",
4527 "pg_proc",
4528 "pg_roles",
4529 "pg_settings",
4530 "pg_trigger",
4531 "pg_type",
4532 "pg_user",
4533 "pg_views",
4534 ];
4535 let name = match self.tokens.get(self.pos) {
4536 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4537 _ => return None,
4538 };
4539 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4542 return None;
4543 }
4544 if !PG_META_TABLES.contains(&name.as_str()) {
4545 return None;
4546 }
4547 self.advance();
4548 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4549 Some(alloc::format!("__spg_pg_{bare}"))
4550 }
4551
4552 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4554 match self.advance() {
4555 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4556 other => Err(ParseError {
4557 message: format!("expected {kw:?}, got {other:?}"),
4558 token_pos: self.pos.saturating_sub(1),
4559 }),
4560 }
4561 }
4562
4563 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4567 match self.advance() {
4568 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4569 other => Err(ParseError {
4570 message: format!("expected identifier or string, got {other:?}"),
4571 token_pos: self.pos.saturating_sub(1),
4572 }),
4573 }
4574 }
4575
4576 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4577 match self.advance() {
4578 Token::String(s) => Ok(s),
4579 other => Err(ParseError {
4580 message: format!("expected quoted string, got {other:?}"),
4581 token_pos: self.pos.saturating_sub(1),
4582 }),
4583 }
4584 }
4585
4586 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4587 self.enter_nested()?;
4591 let r = self.parse_select_stmt_inner();
4592 self.nest_depth -= 1;
4593 r
4594 }
4595
4596 fn parse_select_stmt_inner(&mut self) -> Result<Statement, ParseError> {
4597 let mut head = self.parse_bare_select()?;
4602 while matches!(self.peek(), Token::Union) {
4603 self.advance();
4604 let kind = if matches!(self.peek(), Token::All) {
4605 self.advance();
4606 UnionKind::All
4607 } else {
4608 UnionKind::Distinct
4609 };
4610 let peer = self.parse_bare_select()?;
4611 head.unions.push((kind, peer));
4612 }
4613 head.order_by = if matches!(self.peek(), Token::Order) {
4614 self.advance();
4615 if !matches!(self.peek(), Token::By) {
4616 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4617 }
4618 self.advance();
4619 let mut keys = Vec::new();
4622 loop {
4623 let expr = self.parse_expr(0)?;
4624 let desc = if matches!(self.peek(), Token::Desc) {
4625 self.advance();
4626 true
4627 } else if matches!(self.peek(), Token::Asc) {
4628 self.advance();
4629 false
4630 } else {
4631 false
4632 };
4633 let nulls_first = self.parse_optional_nulls_placement()?;
4635 keys.push(OrderBy {
4636 expr,
4637 desc,
4638 nulls_first,
4639 });
4640 if matches!(self.peek(), Token::Comma) {
4641 self.advance();
4642 } else {
4643 break;
4644 }
4645 }
4646 keys
4647 } else {
4648 Vec::new()
4649 };
4650 head.limit = if matches!(self.peek(), Token::Limit) {
4651 self.advance();
4652 if self.consume_limit_unbounded_sentinel() {
4659 None
4660 } else {
4661 Some(self.parse_limit_expr("LIMIT")?)
4662 }
4663 } else {
4664 None
4665 };
4666 head.offset = if matches!(self.peek(), Token::Offset) {
4667 self.advance();
4668 let off = self.parse_limit_expr("OFFSET")?;
4672 self.consume_optional_rows_keyword();
4673 Some(off)
4674 } else {
4675 None
4676 };
4677 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4683 {
4684 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4687 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4688 {
4689 self.advance();
4690 }
4691 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4694 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4695 {
4696 crate::ast::LimitExpr::Literal(1)
4698 } else {
4699 self.parse_limit_expr("FETCH FIRST")?
4700 };
4701 self.consume_optional_rows_keyword();
4703 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4709 if s.eq_ignore_ascii_case("only"))
4710 {
4711 self.advance();
4712 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4713 if s.eq_ignore_ascii_case("with"))
4714 {
4715 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4717 if s.eq_ignore_ascii_case("ties"))
4718 {
4719 self.advance();
4720 head.limit_with_ties = true;
4721 }
4722 }
4723 head.limit = Some(count);
4724 }
4725 self.consume_optional_for_lock_clauses();
4740 Ok(Statement::Select(head))
4741 }
4742
4743 fn consume_optional_for_lock_clauses(&mut self) {
4750 while matches!(self.peek(), Token::For) {
4751 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4755 if s.eq_ignore_ascii_case("no"))
4756 {
4757 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4761 if s.eq_ignore_ascii_case("key"))
4762 {
4763 self.advance(); }
4765 }
4766 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4768 if s.eq_ignore_ascii_case("key"))
4769 {
4770 self.advance(); }
4772 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4778 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4779 {
4780 self.advance();
4781 } else {
4782 return;
4787 }
4788 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4791 if s.eq_ignore_ascii_case("of"))
4792 {
4793 self.advance(); #[allow(clippy::while_let_loop)]
4795 loop {
4796 match self.peek() {
4797 Token::Ident(_) | Token::QuotedIdent(_) => {
4798 self.advance();
4799 if matches!(self.peek(), Token::Dot) {
4801 self.advance();
4802 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4803 self.advance();
4804 }
4805 }
4806 }
4807 _ => break,
4808 }
4809 if matches!(self.peek(), Token::Comma) {
4810 self.advance();
4811 } else {
4812 break;
4813 }
4814 }
4815 }
4816 match self.peek().clone() {
4818 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4819 self.advance();
4820 }
4821 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4822 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4824 if s.eq_ignore_ascii_case("locked"))
4825 {
4826 self.advance(); }
4828 }
4829 _ => {}
4830 }
4831 }
4833 }
4834
4835 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4844 if matches!(self.peek(), Token::Null) {
4845 self.advance();
4846 return true;
4847 }
4848 if matches!(self.peek(), Token::All) {
4849 self.advance();
4850 return true;
4851 }
4852 false
4853 }
4854
4855 fn consume_optional_rows_keyword(&mut self) {
4859 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4860 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4861 {
4862 self.advance();
4863 }
4864 }
4865
4866 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4867 match self.advance() {
4868 Token::Integer(n) if n >= 0 => u32::try_from(n)
4869 .map(crate::ast::LimitExpr::Literal)
4870 .map_err(|_| ParseError {
4871 message: alloc::format!("{label} value too large: {n}"),
4872 token_pos: self.pos.saturating_sub(1),
4873 }),
4874 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4875 other => Err(ParseError {
4876 message: alloc::format!(
4877 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4878 ),
4879 token_pos: self.pos.saturating_sub(1),
4880 }),
4881 }
4882 }
4883
4884 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4889 if !matches!(self.peek(), Token::Select) {
4890 return Err(self.err(format!(
4891 "expected SELECT to start a query block, got {:?}",
4892 self.peek()
4893 )));
4894 }
4895 self.advance();
4896 let distinct = if matches!(self.peek(), Token::Distinct) {
4897 self.advance();
4898 true
4899 } else {
4900 false
4901 };
4902 let items = self.parse_select_list()?;
4903 let from = if matches!(self.peek(), Token::From) {
4904 self.advance();
4905 Some(self.parse_from_clause()?)
4906 } else {
4907 None
4908 };
4909 let where_ = if matches!(self.peek(), Token::Where) {
4910 self.advance();
4911 Some(self.parse_expr(0)?)
4912 } else {
4913 None
4914 };
4915 let mut group_by_all = false;
4916 let group_by = if matches!(self.peek(), Token::Group) {
4917 self.advance();
4918 if !matches!(self.peek(), Token::By) {
4919 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4920 }
4921 self.advance();
4922 if matches!(self.peek(), Token::All) {
4925 self.advance();
4926 group_by_all = true;
4927 None
4928 } else {
4929 let mut groups = Vec::new();
4930 loop {
4931 groups.push(self.parse_expr(0)?);
4932 if matches!(self.peek(), Token::Comma) {
4933 self.advance();
4934 } else {
4935 break;
4936 }
4937 }
4938 Some(groups)
4939 }
4940 } else {
4941 None
4942 };
4943 let having = if matches!(self.peek(), Token::Having) {
4944 self.advance();
4945 Some(self.parse_expr(0)?)
4946 } else {
4947 None
4948 };
4949 Ok(SelectStatement {
4950 ctes: Vec::new(),
4951 distinct,
4952 items,
4953 from,
4954 where_,
4955 group_by,
4956 group_by_all,
4957 having,
4958 unions: Vec::new(),
4959 order_by: Vec::new(),
4960 limit: None,
4961 offset: None,
4962 limit_with_ties: false,
4963 })
4964 }
4965
4966 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4967 debug_assert!(matches!(self.peek(), Token::Table));
4969 self.advance();
4970 let if_not_exists = self.consume_if_not_exists();
4971 let name = self.expect_ident_like()?;
4972 if matches!(self.peek(), Token::Partition)
4977 && Self::tokens_match_ident_ci(self.tokens.get(self.pos + 1), "of")
4978 {
4979 self.advance(); self.advance(); let partition_of = self.parse_partition_of_tail()?;
4982 return Ok(Statement::CreateTable(CreateTableStatement {
4983 name,
4984 columns: Vec::new(),
4985 if_not_exists,
4986 foreign_keys: Vec::new(),
4987 table_constraints: Vec::new(),
4988 partition_by: None,
4989 partition_of: Some(partition_of),
4990 }));
4991 }
4992 if !matches!(self.peek(), Token::LParen) {
4993 return Err(self.err(format!(
4994 "expected '(' after table name, got {:?}",
4995 self.peek()
4996 )));
4997 }
4998 self.advance();
4999 let mut columns = Vec::new();
5000 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
5001 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
5002 loop {
5003 if self.peek_table_level_pk_start() {
5009 table_constraints.push(self.parse_table_level_primary_key()?);
5010 } else if self.peek_table_level_unique_start() {
5011 table_constraints.push(self.parse_table_level_unique()?);
5012 } else if self.peek_table_level_check_start() {
5013 table_constraints.push(self.parse_table_level_check()?);
5015 } else if self.peek_mysql_inline_key_start() {
5016 if let Some(uc) = self.parse_mysql_inline_key()? {
5022 table_constraints.push(uc);
5023 }
5024 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
5025 self.advance(); let _name = self.expect_ident_like()?;
5033 table_constraints.push(match kind {
5034 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
5035 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
5036 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
5037 });
5038 } else if self.peek_constraint_or_fk_start() {
5039 foreign_keys.push(self.parse_table_level_fk()?);
5040 } else {
5041 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
5042 if col.is_unique {
5046 table_constraints.push(crate::ast::TableConstraint::Unique {
5047 name: None,
5048 columns: alloc::vec![col.name.clone()],
5049 nulls_not_distinct: false,
5050 });
5051 }
5052 if let Some(check_expr) = col.check.clone() {
5053 table_constraints.push(crate::ast::TableConstraint::Check {
5054 name: None,
5055 expr: check_expr,
5056 });
5057 }
5058 columns.push(col);
5059 if let Some(fk) = col_level_fk {
5060 foreign_keys.push(fk);
5061 }
5062 }
5063 match self.peek() {
5064 Token::Comma => {
5065 self.advance();
5066 }
5067 Token::RParen => {
5068 self.advance();
5069 break;
5070 }
5071 other => {
5072 return Err(
5073 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
5074 );
5075 }
5076 }
5077 }
5078 if columns.is_empty() {
5079 return Err(self.err("CREATE TABLE requires at least one column".into()));
5080 }
5081 self.consume_mysql_table_options();
5088 let partition_by = if matches!(self.peek(), Token::Partition) {
5094 self.advance(); if !matches!(self.peek(), Token::By) {
5096 return Err(self.err(format!(
5097 "expected BY after PARTITION, got {:?}",
5098 self.peek()
5099 )));
5100 }
5101 self.advance();
5102 Some(self.parse_partition_by_tail()?)
5103 } else {
5104 None
5105 };
5106 Ok(Statement::CreateTable(CreateTableStatement {
5107 name,
5108 columns,
5109 if_not_exists,
5110 foreign_keys,
5111 table_constraints,
5112 partition_by,
5113 partition_of: None,
5114 }))
5115 }
5116
5117 fn tokens_match_ident_ci(t: Option<&Token>, want: &str) -> bool {
5123 matches!(t, Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case(want))
5124 }
5125
5126 fn parse_partition_by_tail(&mut self) -> Result<crate::ast::PartitionBySpec, ParseError> {
5128 use crate::ast::{PartitionBySpec, PartitionKindAst};
5129 let kind = match self.peek() {
5130 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("range") => {
5131 self.advance();
5132 PartitionKindAst::Range
5133 }
5134 other => {
5135 return Err(self.err(format!(
5136 "PARTITION BY: only RANGE is supported at v7.37.6-B, got {other:?}"
5137 )));
5138 }
5139 };
5140 if !matches!(self.peek(), Token::LParen) {
5141 return Err(self.err(format!(
5142 "expected '(' after PARTITION BY RANGE, got {:?}",
5143 self.peek()
5144 )));
5145 }
5146 self.advance();
5147 let mut key_columns = Vec::new();
5148 loop {
5149 key_columns.push(self.expect_ident_like()?);
5150 match self.peek() {
5151 Token::Comma => {
5152 self.advance();
5153 }
5154 Token::RParen => {
5155 self.advance();
5156 break;
5157 }
5158 other => {
5159 return Err(self.err(format!(
5160 "expected ',' or ')' in PARTITION BY key list, got {other:?}"
5161 )));
5162 }
5163 }
5164 }
5165 if key_columns.is_empty() {
5166 return Err(self.err("PARTITION BY RANGE requires at least one key column".to_string()));
5167 }
5168 Ok(PartitionBySpec { kind, key_columns })
5169 }
5170
5171 fn parse_partition_of_tail(&mut self) -> Result<crate::ast::PartitionOfSpec, ParseError> {
5176 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
5177 let parent_name = self.expect_ident_like()?;
5178 if matches!(self.peek(), Token::LParen) {
5184 return Err(self.err(
5185 "CREATE TABLE … PARTITION OF parent: explicit column list not supported \
5186 at v7.37.6-B; the child inherits its columns from the parent"
5187 .to_string(),
5188 ));
5189 }
5190 let bounds = match self.peek() {
5191 Token::Default => {
5192 self.advance();
5193 PartitionOfBoundsAst::Default
5194 }
5195 Token::For => {
5196 self.advance();
5197 if !matches!(self.peek(), Token::Values) {
5198 return Err(
5199 self.err(format!("expected VALUES after FOR, got {:?}", self.peek()))
5200 );
5201 }
5202 self.advance();
5203 if !matches!(self.peek(), Token::From) {
5204 return Err(self.err(format!(
5205 "expected FROM after FOR VALUES, got {:?}",
5206 self.peek()
5207 )));
5208 }
5209 self.advance();
5210 let lower = Box::new(self.parse_partition_bound_expr()?);
5211 if !matches!(self.peek(), Token::To) {
5212 return Err(self.err(format!(
5213 "expected TO after FROM (...), got {:?}",
5214 self.peek()
5215 )));
5216 }
5217 self.advance();
5218 let upper = Box::new(self.parse_partition_bound_expr()?);
5219 PartitionOfBoundsAst::Range { lower, upper }
5220 }
5221 other => {
5222 return Err(self.err(format!(
5223 "expected FOR VALUES or DEFAULT after PARTITION OF parent, got {other:?}"
5224 )));
5225 }
5226 };
5227 Ok(PartitionOfSpec {
5228 parent_name,
5229 bounds,
5230 })
5231 }
5232
5233 fn parse_partition_bound_expr(&mut self) -> Result<crate::ast::Expr, ParseError> {
5238 if !matches!(self.peek(), Token::LParen) {
5239 return Err(self.err(format!(
5240 "expected '(' before partition bound, got {:?}",
5241 self.peek()
5242 )));
5243 }
5244 self.advance();
5245 let expr = match self.peek() {
5246 Token::Ident(s) | Token::QuotedIdent(s)
5247 if s.eq_ignore_ascii_case("minvalue") || s.eq_ignore_ascii_case("maxvalue") =>
5248 {
5249 let name = s.to_ascii_uppercase();
5250 self.advance();
5251 crate::ast::Expr::FunctionCall {
5252 name,
5253 args: Vec::new(),
5254 }
5255 }
5256 _ => self.parse_expr(0)?,
5257 };
5258 if !matches!(self.peek(), Token::RParen) {
5259 return Err(self.err(format!(
5260 "expected ')' after partition bound, got {:?}",
5261 self.peek()
5262 )));
5263 }
5264 self.advance();
5265 Ok(expr)
5266 }
5267
5268 fn peek_mysql_inline_key_start(&self) -> bool {
5277 let cur = self.peek();
5278 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
5288 match self.tokens.get(skip) {
5291 Some(Token::LParen) => true,
5292 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
5293 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
5294 }
5295 _ => false,
5296 }
5297 };
5298 let is_key_or_index_tok = |t: &Token| -> bool {
5302 matches!(t, Token::Index)
5303 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
5304 };
5305 match cur {
5306 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
5307 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5308 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
5309 }
5310 Token::Ident(s)
5311 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
5312 {
5313 let nxt = self.tokens.get(self.pos + 1);
5314 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
5315 self.pos + 2
5316 } else {
5317 self.pos + 1
5318 };
5319 after_keyword_followed_by_paren_or_ident_paren(after_after)
5320 }
5321 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
5322 let nxt = self.tokens.get(self.pos + 1);
5323 if !nxt.is_some_and(is_key_or_index_tok) {
5324 return false;
5325 }
5326 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
5327 }
5328 _ => false,
5329 }
5330 }
5331
5332 fn parse_mysql_inline_key(
5341 &mut self,
5342 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
5343 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
5345 {
5346 self.advance();
5347 true
5348 } else {
5349 false
5350 };
5351 let mut is_fulltext = false;
5357 let mut is_spatial = false;
5358 if let Token::Ident(s) = self.peek().clone() {
5359 if s.eq_ignore_ascii_case("fulltext") {
5360 self.advance();
5361 is_fulltext = true;
5362 } else if s.eq_ignore_ascii_case("spatial") {
5363 self.advance();
5364 is_spatial = true;
5365 }
5366 }
5367 match self.peek() {
5370 Token::Index => {
5371 self.advance();
5372 }
5373 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5374 self.advance();
5375 }
5376 other => {
5377 return Err(self.err(alloc::format!(
5378 "expected KEY/INDEX in inline index declaration, got {other:?}"
5379 )));
5380 }
5381 }
5382 let mut idx_name: Option<String> = None;
5387 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
5388 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
5389 {
5390 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
5391 idx_name = Some(s);
5392 }
5393 }
5394 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5396 self.advance();
5397 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5398 self.advance();
5399 }
5400 }
5401 if !matches!(self.peek(), Token::LParen) {
5403 return Err(self.err(alloc::format!(
5404 "expected '(' in inline KEY/INDEX, got {:?}",
5405 self.peek()
5406 )));
5407 }
5408 self.advance();
5409 let mut cols: Vec<String> = Vec::new();
5410 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5411 self.advance();
5412 cols.push(s);
5413 if matches!(self.peek(), Token::LParen) {
5415 let mut depth = 1usize;
5416 self.advance();
5417 while depth > 0 {
5418 match self.peek() {
5419 Token::LParen => depth += 1,
5420 Token::RParen => depth -= 1,
5421 Token::Eof => break,
5422 _ => {}
5423 }
5424 self.advance();
5425 }
5426 }
5427 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5429 || matches!(self.peek(), Token::Asc | Token::Desc)
5430 {
5431 self.advance();
5432 }
5433 if matches!(self.peek(), Token::Comma) {
5434 self.advance();
5435 continue;
5436 }
5437 break;
5438 }
5439 if matches!(self.peek(), Token::RParen) {
5440 self.advance();
5441 }
5442 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5445 self.advance();
5446 }
5447 if cols.is_empty() {
5448 return Ok(None);
5449 }
5450 if is_unique {
5451 Ok(Some(crate::ast::TableConstraint::Unique {
5457 name: idx_name,
5458 columns: cols,
5459 nulls_not_distinct: false,
5460 }))
5461 } else if is_fulltext {
5462 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5468 name: idx_name,
5469 columns: cols,
5470 }))
5471 } else if is_spatial {
5472 Ok(None)
5475 } else {
5476 Ok(Some(crate::ast::TableConstraint::Index {
5479 name: idx_name,
5480 columns: cols,
5481 }))
5482 }
5483 }
5484
5485 fn consume_mysql_table_options(&mut self) {
5490 loop {
5491 let name_lc = match self.peek().clone() {
5495 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5496 Token::Default => alloc::string::String::from("default"),
5497 _ => break,
5498 };
5499 let known = matches!(
5500 name_lc.as_str(),
5501 "engine"
5502 | "default"
5503 | "charset"
5504 | "collate"
5505 | "auto_increment"
5506 | "row_format"
5507 | "comment"
5508 | "pack_keys"
5509 | "stats_persistent"
5510 | "stats_auto_recalc"
5511 | "stats_sample_pages"
5512 | "key_block_size"
5513 | "tablespace"
5514 | "min_rows"
5515 | "max_rows"
5516 | "checksum"
5517 | "delay_key_write"
5518 | "insert_method"
5519 | "data"
5520 | "index"
5521 | "encryption"
5522 | "compression"
5523 );
5524 if !known {
5525 break;
5526 }
5527 self.advance(); if name_lc == "default" {
5531 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5532 self.advance();
5533 }
5534 }
5535 if matches!(self.peek(), Token::Eq) {
5536 self.advance();
5537 }
5538 match self.peek() {
5539 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5540 self.advance();
5541 }
5542 _ => {}
5543 }
5544 }
5545 }
5546
5547 fn peek_table_level_pk_start(&self) -> bool {
5552 let cur = self.peek();
5553 let nxt = self.tokens.get(self.pos + 1);
5554 let nxt2 = self.tokens.get(self.pos + 2);
5555 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5556 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5557 let is_lparen = matches!(nxt2, Some(Token::LParen));
5558 is_primary && is_key && is_lparen
5559 }
5560
5561 fn peek_table_level_unique_start(&self) -> bool {
5565 let cur = self.peek();
5566 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5567 if !is_unique {
5568 return false;
5569 }
5570 let n1 = self.tokens.get(self.pos + 1);
5571 if matches!(n1, Some(Token::LParen)) {
5573 return true;
5574 }
5575 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5577 if !is_nulls {
5578 return false;
5579 }
5580 let n2 = self.tokens.get(self.pos + 2);
5581 let n3 = self.tokens.get(self.pos + 3);
5582 let n4 = self.tokens.get(self.pos + 4);
5583 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5585 return true;
5586 }
5587 if matches!(n2, Some(Token::Not))
5589 && matches!(n3, Some(Token::Distinct))
5590 && matches!(n4, Some(Token::LParen))
5591 {
5592 return true;
5593 }
5594 false
5595 }
5596
5597 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5598 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5601 Ok(crate::ast::TableConstraint::PrimaryKey {
5602 name: None,
5603 columns,
5604 })
5605 }
5606
5607 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5608 self.advance(); let mut nulls_not_distinct = false;
5613 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5614 let n1 = self.tokens.get(self.pos + 1);
5615 let n2 = self.tokens.get(self.pos + 2);
5616 let is_not = matches!(n1, Some(Token::Not));
5617 let is_distinct = matches!(n2, Some(Token::Distinct));
5618 if is_not && is_distinct {
5619 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5623 } else if matches!(n1, Some(Token::Distinct)) {
5624 self.advance(); self.advance(); }
5627 }
5628 let columns = self.parse_paren_ident_list("UNIQUE")?;
5629 Ok(crate::ast::TableConstraint::Unique {
5630 name: None,
5631 columns,
5632 nulls_not_distinct,
5633 })
5634 }
5635
5636 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5640 self.advance(); if !matches!(self.peek(), Token::LParen) {
5642 return Err(self.err(alloc::format!(
5643 "expected '(' after CHECK, got {:?}",
5644 self.peek()
5645 )));
5646 }
5647 self.advance();
5648 let expr = self.parse_expr(0)?;
5649 if !matches!(self.peek(), Token::RParen) {
5650 return Err(self.err(alloc::format!(
5651 "expected ')' to close CHECK predicate, got {:?}",
5652 self.peek()
5653 )));
5654 }
5655 self.advance();
5656 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5657 }
5658
5659 fn peek_table_level_check_start(&self) -> bool {
5661 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5662 }
5663
5664 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5669 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5670 return None;
5671 }
5672 match self.tokens.get(self.pos + 2) {
5675 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5676 Some(NamedTableConstraintKind::Check)
5677 }
5678 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5679 Some(NamedTableConstraintKind::Unique)
5680 }
5681 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5682 Some(NamedTableConstraintKind::PrimaryKey)
5683 }
5684 _ => None,
5685 }
5686 }
5687
5688 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5689 if !matches!(self.peek(), Token::LParen) {
5690 return Err(self.err(alloc::format!(
5691 "expected '(' after {ctx}, got {:?}",
5692 self.peek()
5693 )));
5694 }
5695 self.advance();
5696 let mut out = Vec::new();
5697 loop {
5698 out.push(self.expect_ident_like()?);
5699 match self.peek() {
5700 Token::Comma => {
5701 self.advance();
5702 }
5703 Token::RParen => {
5704 self.advance();
5705 break;
5706 }
5707 other => {
5708 return Err(self.err(alloc::format!(
5709 "expected ',' or ')' in {ctx} list, got {other:?}"
5710 )));
5711 }
5712 }
5713 }
5714 if out.is_empty() {
5715 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5716 }
5717 Ok(out)
5718 }
5719
5720 fn peek_constraint_or_fk_start(&self) -> bool {
5725 let is_constraint_kw = matches!(
5726 self.peek(),
5727 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5728 );
5729 let is_foreign_kw = matches!(
5730 self.peek(),
5731 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5732 );
5733 is_constraint_kw || is_foreign_kw
5734 }
5735
5736 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5740 let mut name: Option<String> = None;
5741 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5742 self.advance();
5743 name = Some(self.expect_ident_like()?);
5744 }
5745 match self.advance() {
5747 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5748 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5749 }
5750 match self.advance() {
5752 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5753 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5754 }
5755 if !matches!(self.peek(), Token::LParen) {
5757 return Err(self.err(format!(
5758 "expected '(' after FOREIGN KEY, got {:?}",
5759 self.peek()
5760 )));
5761 }
5762 self.advance();
5763 let mut columns = Vec::new();
5764 loop {
5765 columns.push(self.expect_ident_like()?);
5766 match self.peek() {
5767 Token::Comma => {
5768 self.advance();
5769 }
5770 Token::RParen => {
5771 self.advance();
5772 break;
5773 }
5774 other => {
5775 return Err(self.err(format!(
5776 "expected ',' or ')' in FK column list, got {other:?}"
5777 )));
5778 }
5779 }
5780 }
5781 if columns.is_empty() {
5782 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5783 }
5784 let (parent_table, parent_columns, on_delete, on_update) =
5785 self.parse_references_tail(columns.len())?;
5786 Ok(ForeignKeyConstraint {
5787 name,
5788 columns,
5789 parent_table,
5790 parent_columns,
5791 on_delete,
5792 on_update,
5793 })
5794 }
5795
5796 fn parse_references_tail(
5801 &mut self,
5802 expected_arity: usize,
5803 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5804 match self.advance() {
5805 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5806 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5807 }
5808 let parent_table = self.expect_ident_like()?;
5809 let mut parent_columns: Vec<String> = Vec::new();
5810 if matches!(self.peek(), Token::LParen) {
5811 self.advance();
5812 loop {
5813 parent_columns.push(self.expect_ident_like()?);
5814 match self.peek() {
5815 Token::Comma => {
5816 self.advance();
5817 }
5818 Token::RParen => {
5819 self.advance();
5820 break;
5821 }
5822 other => {
5823 return Err(self.err(format!(
5824 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5825 )));
5826 }
5827 }
5828 }
5829 }
5830 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5831 return Err(self.err(format!(
5832 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5833 expected_arity,
5834 parent_columns.len()
5835 )));
5836 }
5837 let mut on_delete = FkAction::Restrict;
5850 let mut on_update = FkAction::Restrict;
5851 let mut seen_on_delete = false;
5852 let mut seen_on_update = false;
5853 loop {
5854 let before = self.pos;
5856 self.consume_optional_deferrable_clauses()?;
5857 if self.pos != before {
5858 continue;
5859 }
5860 if !matches!(self.peek(), Token::On) {
5862 break;
5863 }
5864 self.advance();
5865 let which = self.advance();
5866 let action = self.parse_fk_action()?;
5867 match which {
5868 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5869 if seen_on_delete {
5870 return Err(self.err("ON DELETE specified twice".into()));
5871 }
5872 seen_on_delete = true;
5873 on_delete = action;
5874 }
5875 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5876 if seen_on_update {
5877 return Err(self.err("ON UPDATE specified twice".into()));
5878 }
5879 seen_on_update = true;
5880 on_update = action;
5881 }
5882 other => {
5883 return Err(
5884 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5885 );
5886 }
5887 }
5888 }
5889 Ok((parent_table, parent_columns, on_delete, on_update))
5890 }
5891
5892 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5895 match self.advance() {
5896 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5897 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5898 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5899 Token::Null => Ok(FkAction::SetNull),
5900 Token::Default => Ok(FkAction::SetDefault),
5901 other => Err(self.err(format!(
5902 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5903 ))),
5904 },
5905 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5906 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5907 other => Err(self.err(format!(
5908 "expected ACTION after NO in FK action, got {other:?}"
5909 ))),
5910 },
5911 other => Err(self.err(format!(
5912 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5913 ))),
5914 }
5915 }
5916
5917 fn consume_if_not_exists(&mut self) -> bool {
5920 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5924 if !looks_like_if {
5925 return false;
5926 }
5927 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5930 return false;
5931 }
5932 if !matches!(
5933 self.tokens.get(self.pos + 2),
5934 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5935 ) {
5936 return false;
5937 }
5938 self.advance(); self.advance(); self.advance(); true
5942 }
5943
5944 fn consume_if_exists(&mut self) -> bool {
5948 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5949 if !looks_like_if {
5950 return false;
5951 }
5952 if !matches!(
5953 self.tokens.get(self.pos + 1),
5954 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5955 ) {
5956 return false;
5957 }
5958 self.advance(); self.advance(); true
5961 }
5962
5963 fn parse_optional_nulls_placement(&mut self) -> Result<Option<bool>, ParseError> {
5971 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5972 return Ok(None);
5973 }
5974 self.advance();
5975 match self.advance() {
5976 Token::Ident(s) if s.eq_ignore_ascii_case("first") => Ok(Some(true)),
5977 Token::Ident(s) if s.eq_ignore_ascii_case("last") => Ok(Some(false)),
5978 other => Err(self.err(alloc::format!(
5979 "expected FIRST or LAST after NULLS, got {other:?}"
5980 ))),
5981 }
5982 }
5983
5984 fn consume_optional_index_column_qualifiers(&mut self) {
5985 loop {
5986 match self.peek() {
5987 Token::Asc | Token::Desc => {
5988 self.advance();
5989 }
5990 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5991 let look = self.tokens.get(self.pos + 1);
5992 if matches!(
5993 look,
5994 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5995 || k.eq_ignore_ascii_case("last")
5996 ) {
5997 self.advance();
5998 self.advance();
5999 } else {
6000 break;
6001 }
6002 }
6003 _ => break,
6004 }
6005 }
6006 }
6007
6008 fn parse_create_index_stmt_after_create(
6009 &mut self,
6010 is_unique: bool,
6011 ) -> Result<Statement, ParseError> {
6012 debug_assert!(matches!(self.peek(), Token::Index));
6014 self.advance();
6015 let if_not_exists = self.consume_if_not_exists();
6016 let name = self.expect_ident_like()?;
6017 if !matches!(self.peek(), Token::On) {
6018 return Err(self.err(format!(
6019 "expected ON after CREATE INDEX <name>, got {:?}",
6020 self.peek()
6021 )));
6022 }
6023 self.advance();
6024 let table = self.expect_ident_like()?;
6025 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6030 self.advance();
6031 let m = self.expect_ident_like()?;
6032 match m.to_ascii_lowercase().as_str() {
6033 "hnsw" => IndexMethod::Hnsw,
6034 "btree" => IndexMethod::BTree,
6035 "brin" => IndexMethod::Brin,
6036 "gin" => IndexMethod::Gin,
6041 "gist" | "spgist" | "hash" => IndexMethod::BTree,
6050 "ivfflat" => IndexMethod::Hnsw,
6058 other => {
6059 return Err(self.err(alloc::format!(
6060 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
6061 )));
6062 }
6063 }
6064 } else {
6065 IndexMethod::BTree
6066 };
6067 if !matches!(self.peek(), Token::LParen) {
6068 return Err(self.err(format!(
6069 "expected '(' before indexed column, got {:?}",
6070 self.peek()
6071 )));
6072 }
6073 self.advance();
6074 let mut opclass: Option<String> = None;
6083 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
6084 Token::Ident(s) | Token::QuotedIdent(s)
6091 if matches!(
6092 self.tokens.get(self.pos + 1),
6093 Some(Token::RParen | Token::Comma)
6094 ) =>
6095 {
6096 self.advance();
6097 (s, None)
6098 }
6099 Token::Ident(s) | Token::QuotedIdent(s)
6113 if matches!(
6114 self.tokens.get(self.pos + 1),
6115 Some(Token::Ident(_) | Token::QuotedIdent(_))
6116 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
6117 && matches!(
6118 self.tokens.get(self.pos + 3),
6119 Some(Token::Ident(op) | Token::QuotedIdent(op))
6120 if is_vector_opclass_name(op)
6121 ) =>
6122 {
6123 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
6127 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6128 opclass = Some(op.to_ascii_lowercase());
6129 }
6130 (s, None)
6131 }
6132 Token::Ident(s) | Token::QuotedIdent(s)
6133 if matches!(
6134 self.tokens.get(self.pos + 1),
6135 Some(Token::Ident(op) | Token::QuotedIdent(op))
6136 if is_vector_opclass_name(op)
6137 ) =>
6138 {
6139 self.advance(); let op_tok = self.advance();
6143 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6144 opclass = Some(op.to_ascii_lowercase());
6145 }
6146 (s, None)
6147 }
6148 Token::Ident(_) | Token::QuotedIdent(_) => {
6149 let key_expr = self.parse_expr(0)?;
6150 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6151 self.err("expression index key must reference at least one column".into())
6152 })?;
6153 (primary, Some(key_expr))
6154 }
6155 Token::LParen => {
6163 let key_expr = self.parse_expr(0)?;
6164 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6165 self.err("expression index key must reference at least one column".into())
6166 })?;
6167 (primary, Some(key_expr))
6168 }
6169 other => {
6170 return Err(self.err(format!(
6171 "expected column ident or expression, got {other:?}"
6172 )));
6173 }
6174 };
6175 let mut extra_columns: Vec<String> = Vec::new();
6184 self.consume_optional_index_column_qualifiers();
6186 while matches!(self.peek(), Token::Comma) {
6187 self.advance();
6188 let extra = self.expect_ident_like()?;
6189 self.consume_optional_index_column_qualifiers();
6190 extra_columns.push(extra);
6191 }
6192 if !matches!(self.peek(), Token::RParen) {
6193 return Err(self.err(format!(
6194 "expected ')' after indexed column / expression, got {:?}",
6195 self.peek()
6196 )));
6197 }
6198 self.advance();
6199 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
6203 {
6204 self.advance();
6205 if !matches!(self.peek(), Token::LParen) {
6206 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
6207 }
6208 self.advance();
6209 let mut cols = Vec::new();
6210 loop {
6211 cols.push(self.expect_ident_like()?);
6212 match self.peek() {
6213 Token::Comma => {
6214 self.advance();
6215 }
6216 Token::RParen => {
6217 self.advance();
6218 break;
6219 }
6220 other => {
6221 return Err(self.err(format!(
6222 "expected ',' or ')' in INCLUDE list, got {other:?}"
6223 )));
6224 }
6225 }
6226 }
6227 cols
6228 } else {
6229 Vec::new()
6230 };
6231 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
6237 self.advance();
6238 if !matches!(self.peek(), Token::LParen) {
6239 return Err(self.err(format!(
6240 "expected '(' after WITH in CREATE INDEX, got {:?}",
6241 self.peek()
6242 )));
6243 }
6244 self.advance();
6245 loop {
6246 if matches!(self.peek(), Token::RParen) {
6247 self.advance();
6248 break;
6249 }
6250 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
6253 self.advance();
6254 let _ = self.advance(); }
6256 match self.peek() {
6257 Token::Comma => {
6258 self.advance();
6259 }
6260 Token::RParen => {
6261 self.advance();
6262 break;
6263 }
6264 other => {
6265 return Err(self.err(format!(
6266 "expected ',' or ')' in WITH (…) clause, got {other:?}"
6267 )));
6268 }
6269 }
6270 }
6271 }
6272 let partial_predicate = if matches!(self.peek(), Token::Where) {
6274 self.advance();
6275 Some(self.parse_expr(0)?)
6276 } else {
6277 None
6278 };
6279 if is_unique && !matches!(method, IndexMethod::BTree) {
6284 return Err(self.err(alloc::format!(
6285 "UNIQUE is only supported on BTree indexes, got USING {:?}",
6286 method
6287 )));
6288 }
6289 Ok(Statement::CreateIndex(CreateIndexStatement {
6290 name,
6291 table,
6292 column,
6293 method,
6294 if_not_exists,
6295 included_columns,
6296 partial_predicate,
6297 extra_columns: extra_columns.clone(),
6298 expression,
6299 is_unique,
6300 opclass,
6301 }))
6302 }
6303
6304 fn parse_column_def_with_fk(
6309 &mut self,
6310 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
6311 let col = self.parse_column_def()?;
6312 let inline_references = matches!(
6314 self.peek(),
6315 Token::Ident(s) if s.eq_ignore_ascii_case("references")
6316 );
6317 if !inline_references {
6318 return Ok((col, None));
6319 }
6320 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
6321 let fk = ForeignKeyConstraint {
6322 name: None,
6323 columns: vec![col.name.clone()],
6324 parent_table,
6325 parent_columns,
6326 on_delete,
6327 on_update,
6328 };
6329 Ok((col, Some(fk)))
6330 }
6331
6332 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
6340 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
6341 Ok(ty)
6342 }
6343
6344 #[allow(clippy::type_complexity)]
6345 fn parse_type_with_implied_flags(
6346 &mut self,
6347 ) -> Result<
6348 (
6349 ColumnTypeName,
6350 bool,
6351 bool,
6352 Option<String>,
6353 Collation,
6354 bool,
6355 Option<Vec<String>>,
6359 Option<Vec<String>>,
6362 ),
6363 ParseError,
6364 > {
6365 let mut ty_ident = match self.advance() {
6366 Token::Ident(s) => s,
6367 Token::Interval => "interval".to_string(),
6373 other => {
6374 return Err(ParseError {
6375 message: format!("expected column type, got {other:?}"),
6376 token_pos: self.pos.saturating_sub(1),
6377 });
6378 }
6379 };
6380 while matches!(self.peek(), Token::Dot) {
6385 self.advance();
6386 ty_ident = self.expect_ident_like()?;
6387 }
6388 let mut implied_auto_increment = false;
6389 let mut implied_not_null = false;
6390 let mut user_type_ref: Option<String> = None;
6391 let mut inline_enum_variants: Option<Vec<String>> = None;
6396 let mut inline_set_variants: Option<Vec<String>> = None;
6398 let mut ty = match ty_ident.as_str() {
6399 "smallserial" | "serial2" => {
6401 implied_auto_increment = true;
6402 implied_not_null = true;
6403 ColumnTypeName::SmallInt
6404 }
6405 "serial" | "serial4" => {
6406 implied_auto_increment = true;
6407 implied_not_null = true;
6408 ColumnTypeName::Int
6409 }
6410 "bigserial" | "serial8" => {
6411 implied_auto_increment = true;
6412 implied_not_null = true;
6413 ColumnTypeName::BigInt
6414 }
6415 "smallint" => {
6421 self.consume_optional_paren_size();
6426 ColumnTypeName::SmallInt
6427 }
6428 "tinyint" => {
6439 let width = self.peek_optional_paren_size_value();
6440 self.consume_optional_paren_size();
6441 if width == Some(1) {
6442 ColumnTypeName::Bool
6443 } else {
6444 ColumnTypeName::SmallInt
6445 }
6446 }
6447 "int" | "integer" | "mediumint" => {
6448 self.consume_optional_paren_size();
6449 ColumnTypeName::Int
6450 }
6451 "bigint" => {
6452 self.consume_optional_paren_size();
6453 ColumnTypeName::BigInt
6454 }
6455 "float" | "double" | "real" => {
6460 if ty_ident.eq_ignore_ascii_case("double")
6461 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6462 {
6463 self.advance();
6464 }
6465 ColumnTypeName::Float
6466 }
6467 "float4" | "float8" => ColumnTypeName::Float,
6469 "text" => ColumnTypeName::Text,
6470 "bool" | "boolean" => ColumnTypeName::Bool,
6471 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6472 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6473 "vector" => {
6474 let dim = self.parse_paren_size("VECTOR")?;
6475 let encoding = self.parse_optional_vector_encoding()?;
6476 ColumnTypeName::Vector { dim, encoding }
6477 }
6478 "numeric" => {
6479 let (precision, scale) = self.parse_optional_numeric_params()?;
6480 ColumnTypeName::Numeric(precision, scale)
6481 }
6482 "date" => ColumnTypeName::Date,
6483 "timestamp" | "datetime" => {
6486 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6492 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6493 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6494 {
6495 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6499 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6500 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6501 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6502 {
6503 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6507 } else {
6508 self.consume_optional_paren_size();
6512 ColumnTypeName::Timestamp
6513 }
6514 }
6515 "timestamptz" => ColumnTypeName::Timestamptz,
6519 "json" => ColumnTypeName::Json,
6524 "jsonb" => ColumnTypeName::Jsonb,
6525 "bytea" | "bytes" => ColumnTypeName::Bytes,
6531 "tsvector" => ColumnTypeName::TsVector,
6541 "tsquery" => ColumnTypeName::TsQuery,
6542 "uuid" => ColumnTypeName::Uuid,
6546 "interval" => ColumnTypeName::Interval,
6551 "time" => ColumnTypeName::Time,
6554 "year" => ColumnTypeName::Year,
6557 "timetz" => ColumnTypeName::TimeTz,
6560 "money" => ColumnTypeName::Money,
6563 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6565 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6566 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6567 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6568 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6569 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6570 "int4multirange" => ColumnTypeName::Multirange(RangeKindAst::Int4),
6572 "int8multirange" => ColumnTypeName::Multirange(RangeKindAst::Int8),
6573 "nummultirange" => ColumnTypeName::Multirange(RangeKindAst::Num),
6574 "tsmultirange" => ColumnTypeName::Multirange(RangeKindAst::Ts),
6575 "tstzmultirange" => ColumnTypeName::Multirange(RangeKindAst::TsTz),
6576 "datemultirange" => ColumnTypeName::Multirange(RangeKindAst::Date),
6577 "point" => ColumnTypeName::Point,
6579 "lseg" => ColumnTypeName::Lseg,
6580 "path" => ColumnTypeName::Path,
6581 "box" => ColumnTypeName::PgBox,
6582 "polygon" => ColumnTypeName::Polygon,
6583 "line" => ColumnTypeName::Line,
6584 "circle" => ColumnTypeName::Circle,
6585 "inet" => ColumnTypeName::Inet,
6587 "cidr" => ColumnTypeName::Cidr,
6588 "macaddr" => ColumnTypeName::Macaddr,
6589 "macaddr8" => ColumnTypeName::Macaddr8,
6590 "bit" => ColumnTypeName::Bit,
6591 "varbit" => ColumnTypeName::BitVarying,
6592 "xml" => ColumnTypeName::Xml,
6593 "hstore" => ColumnTypeName::Hstore,
6595 "enum" => {
6601 if !matches!(self.peek(), Token::LParen) {
6603 return Err(self.err(alloc::format!(
6604 "expected '(' after ENUM, got {:?}",
6605 self.peek()
6606 )));
6607 }
6608 self.advance();
6609 let mut variants: Vec<String> = Vec::new();
6610 loop {
6611 match self.advance() {
6612 Token::String(s) => variants.push(s),
6613 other => {
6614 return Err(self.err(alloc::format!(
6615 "ENUM(...) expects string literal variants, got {other:?}"
6616 )));
6617 }
6618 }
6619 match self.peek() {
6620 Token::Comma => {
6621 self.advance();
6622 continue;
6623 }
6624 Token::RParen => {
6625 self.advance();
6626 break;
6627 }
6628 other => {
6629 return Err(self.err(alloc::format!(
6630 "expected ',' or ')' in ENUM(...), got {other:?}"
6631 )));
6632 }
6633 }
6634 }
6635 if variants.is_empty() {
6636 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6637 }
6638 inline_enum_variants = Some(variants);
6639 ColumnTypeName::Text
6642 }
6643 "set" => {
6647 if !matches!(self.peek(), Token::LParen) {
6648 return Err(self.err(alloc::format!(
6649 "expected '(' after SET, got {:?}",
6650 self.peek()
6651 )));
6652 }
6653 self.advance();
6654 let mut variants: Vec<String> = Vec::new();
6655 loop {
6656 match self.advance() {
6657 Token::String(s) => variants.push(s),
6658 other => {
6659 return Err(self.err(alloc::format!(
6660 "SET(...) expects string literal variants, got {other:?}"
6661 )));
6662 }
6663 }
6664 match self.peek() {
6665 Token::Comma => {
6666 self.advance();
6667 continue;
6668 }
6669 Token::RParen => {
6670 self.advance();
6671 break;
6672 }
6673 other => {
6674 return Err(self.err(alloc::format!(
6675 "expected ',' or ')' in SET(...), got {other:?}"
6676 )));
6677 }
6678 }
6679 }
6680 if variants.is_empty() {
6681 return Err(self.err("SET(...) must declare at least one variant".into()));
6682 }
6683 inline_set_variants = Some(variants);
6684 ColumnTypeName::Text
6685 }
6686 _other => {
6687 user_type_ref = Some(ty_ident.clone());
6693 ColumnTypeName::Text
6694 }
6695 };
6696 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6706 {
6707 self.advance();
6708 true
6709 } else {
6710 false
6711 };
6712 let mut collation = Collation::Binary;
6728 loop {
6729 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6730 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6731 {
6732 self.advance(); self.advance(); if matches!(
6735 self.peek(),
6736 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6737 ) {
6738 self.advance();
6739 }
6740 continue;
6741 }
6742 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6743 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6750 match this.peek().clone() {
6751 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6752 this.advance();
6753 Some(s)
6754 }
6755 Token::Default => {
6756 this.advance();
6757 Some(alloc::string::String::from("default"))
6758 }
6759 _ => None,
6760 }
6761 };
6762 let raw = if let Some(head) = read_collation_atom(self) {
6763 if matches!(self.peek(), Token::Dot) {
6765 self.advance();
6766 let tail = read_collation_atom(self).unwrap_or_default();
6767 alloc::format!("{head}.{tail}")
6768 } else {
6769 head
6770 }
6771 } else {
6772 alloc::string::String::new()
6773 };
6774 if !raw.is_empty() {
6775 let parsed = Collation::from_collation_name(&raw);
6776 if parsed != Collation::Binary {
6782 collation = parsed;
6783 }
6784 }
6785 continue;
6786 }
6787 break;
6788 }
6789 if matches!(self.peek(), Token::LBracket) {
6794 self.advance();
6795 if !matches!(self.peek(), Token::RBracket) {
6796 return Err(self.err(alloc::format!(
6797 "TEXT[] takes no dimension; got {:?}",
6798 self.peek()
6799 )));
6800 }
6801 self.advance();
6802 ty = match ty {
6806 ColumnTypeName::Text => ColumnTypeName::TextArray,
6807 ColumnTypeName::Int => ColumnTypeName::IntArray,
6808 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6809 ColumnTypeName::Interval => ColumnTypeName::IntervalArray,
6812 ColumnTypeName::Bool => ColumnTypeName::BoolArray,
6814 ColumnTypeName::SmallInt => ColumnTypeName::SmallIntArray,
6815 ColumnTypeName::Float => ColumnTypeName::FloatArray,
6816 ColumnTypeName::Numeric(_, _) => ColumnTypeName::NumericArray,
6820 ColumnTypeName::Date => ColumnTypeName::DateArray,
6821 ColumnTypeName::Timestamp => ColumnTypeName::TimestampArray,
6822 ColumnTypeName::Timestamptz => ColumnTypeName::TimestamptzArray,
6823 ColumnTypeName::Uuid => ColumnTypeName::UuidArray,
6824 ColumnTypeName::Json => ColumnTypeName::JsonArray,
6825 ColumnTypeName::Jsonb => ColumnTypeName::JsonbArray,
6826 ColumnTypeName::Bytes => ColumnTypeName::BytesArray,
6827 ColumnTypeName::Varchar(_) => ColumnTypeName::VarcharArray,
6831 ColumnTypeName::Char(_) => ColumnTypeName::CharArray,
6832 ColumnTypeName::Money => ColumnTypeName::MoneyArray,
6835 other => {
6836 return Err(self.err(alloc::format!("{other:?}[] not yet supported")));
6837 }
6838 };
6839 if matches!(self.peek(), Token::LBracket) {
6842 self.advance();
6843 if !matches!(self.peek(), Token::RBracket) {
6844 return Err(self.err(alloc::format!(
6845 "TYPE[][] second dimension takes no size; got {:?}",
6846 self.peek()
6847 )));
6848 }
6849 self.advance();
6850 ty = match ty {
6851 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6852 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6853 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6854 other => {
6855 return Err(self.err(alloc::format!(
6856 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6857 TEXT[][] only; got {other:?}"
6858 )));
6859 }
6860 };
6861 }
6862 }
6863 Ok((
6864 ty,
6865 implied_auto_increment,
6866 implied_not_null,
6867 user_type_ref,
6868 collation,
6869 is_unsigned,
6870 inline_enum_variants,
6871 inline_set_variants,
6872 ))
6873 }
6874
6875 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6876 if let Token::Ident(s) = self.peek()
6885 && [
6886 "unique",
6887 "primary",
6888 "foreign",
6889 "constraint",
6890 "check",
6891 "references",
6892 "exclude",
6893 ]
6894 .iter()
6895 .any(|kw| s.eq_ignore_ascii_case(kw))
6896 {
6897 return Err(self.err(alloc::format!(
6898 "unexpected reserved keyword '{s}' at start of column definition \
6899 (malformed table constraint?)"
6900 )));
6901 }
6902 let name = self.expect_ident_like()?;
6903 let (
6904 ty,
6905 implied_auto_increment,
6906 implied_not_null,
6907 user_type_ref,
6908 collation,
6909 is_unsigned,
6910 inline_enum_variants,
6911 inline_set_variants,
6912 ) = self.parse_type_with_implied_flags()?;
6913 let mut default: Option<Expr> = None;
6917 let mut nullable = !implied_not_null;
6918 let mut nullability_seen = implied_not_null;
6919 let mut auto_increment = implied_auto_increment;
6920 let mut is_primary_key = false;
6921 let mut is_unique = false;
6922 let mut check: Option<Expr> = None;
6923 let mut on_update_runtime: Option<Expr> = None;
6924 let mut generated_stored_expr: Option<Box<Expr>> = None;
6925 loop {
6926 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
6932 self.advance();
6933 let _name = self.expect_ident_like()?;
6934 continue;
6935 }
6936 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
6946 self.advance();
6947 match self.peek().clone() {
6948 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
6949 self.advance();
6950 }
6951 Token::By => {
6953 self.advance();
6954 if !matches!(self.peek(), Token::Default) {
6955 return Err(self.err(alloc::format!(
6956 "expected DEFAULT after GENERATED BY, got {:?}",
6957 self.peek()
6958 )));
6959 }
6960 self.advance();
6961 }
6962 other => {
6963 return Err(self.err(alloc::format!(
6964 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
6965 )));
6966 }
6967 }
6968 if !matches!(self.peek(), Token::As) {
6969 return Err(self.err(alloc::format!(
6970 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
6971 self.peek()
6972 )));
6973 }
6974 self.advance();
6975 if matches!(self.peek(), Token::LParen) {
6982 self.advance();
6983 let expr = self.parse_expr(0)?;
6984 if !matches!(self.peek(), Token::RParen) {
6985 return Err(self.err(alloc::format!(
6986 "expected ')' after GENERATED ALWAYS AS (<expr>), got {:?}",
6987 self.peek()
6988 )));
6989 }
6990 self.advance();
6991 let stored = match self.peek() {
6992 Token::Ident(s) | Token::QuotedIdent(s)
6993 if s.eq_ignore_ascii_case("stored") =>
6994 {
6995 self.advance();
6996 true
6997 }
6998 Token::Ident(s) | Token::QuotedIdent(s)
6999 if s.eq_ignore_ascii_case("virtual") =>
7000 {
7001 return Err(self.err(
7002 "GENERATED ALWAYS AS (expr) VIRTUAL is not supported \
7003 at v7.37.7; use STORED"
7004 .into(),
7005 ));
7006 }
7007 other => {
7008 return Err(self.err(alloc::format!(
7009 "expected STORED after GENERATED ALWAYS AS (<expr>), \
7010 got {other:?}"
7011 )));
7012 }
7013 };
7014 let _ = stored; generated_stored_expr = Some(Box::new(expr));
7016 continue;
7017 }
7018 self.expect_keyword_ident("identity")?;
7019 if matches!(self.peek(), Token::LParen) {
7023 let mut depth = 0usize;
7024 loop {
7025 match self.advance() {
7026 Token::LParen => depth += 1,
7027 Token::RParen => {
7028 depth -= 1;
7029 if depth == 0 {
7030 break;
7031 }
7032 }
7033 Token::Eof => {
7034 return Err(self.err(
7035 "unterminated sequence-options parens after IDENTITY".into(),
7036 ));
7037 }
7038 _ => {}
7039 }
7040 }
7041 }
7042 auto_increment = true;
7043 nullable = false;
7045 continue;
7046 }
7047 if matches!(self.peek(), Token::On)
7052 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
7053 {
7054 self.advance(); self.advance(); let next = self.peek().clone();
7058 match next {
7059 Token::Ident(s) | Token::QuotedIdent(s)
7060 if s.eq_ignore_ascii_case("current_timestamp") =>
7061 {
7062 self.advance();
7063 if matches!(self.peek(), Token::LParen) {
7065 self.advance();
7066 if !matches!(self.peek(), Token::Integer(_)) {
7067 return Err(self.err(alloc::format!(
7068 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
7069 self.peek()
7070 )));
7071 }
7072 self.advance();
7073 if !matches!(self.peek(), Token::RParen) {
7074 return Err(self.err(alloc::format!(
7075 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
7076 self.peek()
7077 )));
7078 }
7079 self.advance();
7080 }
7081 on_update_runtime = Some(Expr::FunctionCall {
7082 name: "now".into(),
7083 args: Vec::new(),
7084 });
7085 continue;
7086 }
7087 other => {
7088 return Err(self.err(alloc::format!(
7089 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
7090 )));
7091 }
7092 }
7093 }
7094 if matches!(self.peek(), Token::Default) {
7095 if default.is_some() {
7096 return Err(self.err("DEFAULT specified twice".into()));
7097 }
7098 self.advance();
7099 default = Some(self.parse_expr(0)?);
7100 continue;
7101 }
7102 if matches!(self.peek(), Token::Not) {
7103 if nullability_seen {
7104 return Err(self.err("NOT NULL specified twice".into()));
7105 }
7106 self.advance();
7107 if !matches!(self.peek(), Token::Null) {
7108 return Err(self.err(format!(
7109 "expected NULL after NOT in column def, got {:?}",
7110 self.peek()
7111 )));
7112 }
7113 self.advance();
7114 nullable = false;
7115 nullability_seen = true;
7116 continue;
7117 }
7118 if matches!(self.peek(), Token::Null) {
7124 if nullability_seen && !nullable {
7125 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
7126 }
7127 self.advance();
7128 nullable = true;
7129 nullability_seen = true;
7130 continue;
7131 }
7132 if let Token::Ident(s) = self.peek()
7135 && (s.eq_ignore_ascii_case("auto_increment")
7136 || s.eq_ignore_ascii_case("autoincrement"))
7137 {
7138 if auto_increment {
7139 return Err(self.err("AUTO_INCREMENT specified twice".into()));
7140 }
7141 self.advance();
7142 auto_increment = true;
7143 continue;
7144 }
7145 if let Token::Ident(s) = self.peek()
7150 && s.eq_ignore_ascii_case("primary")
7151 {
7152 if is_primary_key {
7153 return Err(self.err("PRIMARY KEY specified twice".into()));
7154 }
7155 let next = self.tokens.get(self.pos + 1);
7157 let next_is_key = matches!(
7158 next,
7159 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
7160 );
7161 if !next_is_key {
7162 return Err(self.err(format!(
7163 "expected KEY after PRIMARY in column def, got {:?}",
7164 next
7165 )));
7166 }
7167 self.advance(); self.advance(); is_primary_key = true;
7170 if nullability_seen && nullable {
7171 return Err(self.err(
7172 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
7173 ));
7174 }
7175 nullable = false;
7176 nullability_seen = true;
7177 continue;
7178 }
7179 if let Token::Ident(s) = self.peek()
7183 && s.eq_ignore_ascii_case("unique")
7184 {
7185 if is_unique {
7186 return Err(self.err("UNIQUE specified twice".into()));
7187 }
7188 self.advance();
7189 is_unique = true;
7190 continue;
7191 }
7192 if let Token::Ident(s) = self.peek()
7197 && s.eq_ignore_ascii_case("check")
7198 {
7199 self.advance();
7200 if !matches!(self.peek(), Token::LParen) {
7201 return Err(self.err(alloc::format!(
7202 "expected '(' after CHECK in column def, got {:?}",
7203 self.peek()
7204 )));
7205 }
7206 self.advance();
7207 let pred = self.parse_expr(0)?;
7208 if !matches!(self.peek(), Token::RParen) {
7209 return Err(self.err(alloc::format!(
7210 "expected ')' to close CHECK predicate, got {:?}",
7211 self.peek()
7212 )));
7213 }
7214 self.advance();
7215 check = Some(match check.take() {
7216 Some(prev) => Expr::Binary {
7217 op: BinOp::And,
7218 lhs: Box::new(prev),
7219 rhs: Box::new(pred),
7220 },
7221 None => pred,
7222 });
7223 continue;
7224 }
7225 break;
7226 }
7227 Ok(ColumnDef {
7228 name,
7229 ty,
7230 nullable,
7231 default,
7232 auto_increment,
7233 is_primary_key,
7234 is_unique,
7235 check,
7236 user_type_ref,
7237 on_update_runtime,
7238 collation,
7239 is_unsigned,
7240 inline_enum_variants,
7241 inline_set_variants,
7242 generated_stored_expr,
7243 })
7244 }
7245
7246 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
7250 if !matches!(self.peek(), Token::LParen) {
7251 return Ok((0, 0));
7255 }
7256 self.advance();
7257 let precision = match self.advance() {
7258 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
7259 other => {
7260 return Err(ParseError {
7261 message: format!(
7262 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
7263 ),
7264 token_pos: self.pos.saturating_sub(1),
7265 });
7266 }
7267 };
7268 let scale = if matches!(self.peek(), Token::Comma) {
7269 self.advance();
7270 match self.advance() {
7271 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
7272 u8::try_from(n).expect("range-checked")
7273 }
7274 other => {
7275 return Err(ParseError {
7276 message: format!(
7277 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
7278 ),
7279 token_pos: self.pos.saturating_sub(1),
7280 });
7281 }
7282 }
7283 } else {
7284 0
7285 };
7286 if !matches!(self.peek(), Token::RParen) {
7287 return Err(self.err(format!(
7288 "expected ')' to close NUMERIC params, got {:?}",
7289 self.peek()
7290 )));
7291 }
7292 self.advance();
7293 Ok((precision, scale))
7294 }
7295
7296 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
7304 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
7305 return Ok(VecEncoding::F32);
7306 }
7307 let n1 = self.tokens.get(self.pos + 1);
7313 let next_is_encoding = matches!(
7314 n1,
7315 Some(Token::Ident(s))
7316 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
7317 );
7318 if !next_is_encoding {
7319 return Ok(VecEncoding::F32);
7320 }
7321 self.advance();
7322 let enc_ident = match self.advance() {
7323 Token::Ident(s) => s,
7324 other => {
7325 return Err(self.err(format!(
7326 "expected vector encoding after USING, got {other:?}"
7327 )));
7328 }
7329 };
7330 match enc_ident.to_ascii_lowercase().as_str() {
7331 "sq8" => Ok(VecEncoding::Sq8),
7332 "half" => Ok(VecEncoding::F16),
7335 other => Err(self.err(format!(
7336 "unknown vector encoding {other:?}; supported: SQ8, HALF"
7337 ))),
7338 }
7339 }
7340
7341 fn peek_optional_paren_size_value(&self) -> Option<i64> {
7347 if !matches!(self.peek(), Token::LParen) {
7348 return None;
7349 }
7350 let next = self.tokens.get(self.pos + 1)?;
7351 let n = match next {
7352 Token::Integer(n) => *n,
7353 _ => return None,
7354 };
7355 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
7356 return None;
7357 }
7358 Some(n)
7359 }
7360
7361 fn consume_optional_paren_size(&mut self) {
7365 if !matches!(self.peek(), Token::LParen) {
7366 return;
7367 }
7368 self.advance();
7369 let mut depth = 1usize;
7371 while depth > 0 {
7372 match self.peek() {
7373 Token::LParen => depth += 1,
7374 Token::RParen => depth -= 1,
7375 Token::Eof => return,
7376 _ => {}
7377 }
7378 self.advance();
7379 }
7380 }
7381
7382 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
7383 if !matches!(self.peek(), Token::LParen) {
7384 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
7385 }
7386 self.advance();
7387 let n = match self.advance() {
7388 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
7389 message: format!("{label} size too large: {n}"),
7390 token_pos: self.pos.saturating_sub(1),
7391 })?,
7392 other => {
7393 return Err(ParseError {
7394 message: format!("expected positive integer {label} size, got {other:?}"),
7395 token_pos: self.pos.saturating_sub(1),
7396 });
7397 }
7398 };
7399 if !matches!(self.peek(), Token::RParen) {
7400 return Err(self.err(format!(
7401 "expected ')' after {label} size, got {:?}",
7402 self.peek()
7403 )));
7404 }
7405 self.advance();
7406 Ok(n)
7407 }
7408
7409 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
7410 debug_assert!(matches!(self.peek(), Token::Insert));
7411 self.advance();
7412 if !matches!(self.peek(), Token::Into) {
7413 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
7414 }
7415 self.advance();
7416 let table = self.expect_ident_like()?;
7417 let columns = if matches!(self.peek(), Token::LParen) {
7419 self.advance();
7420 let mut names = Vec::new();
7421 loop {
7422 names.push(self.expect_ident_like()?);
7423 match self.peek() {
7424 Token::Comma => {
7425 self.advance();
7426 }
7427 Token::RParen => {
7428 self.advance();
7429 break;
7430 }
7431 other => {
7432 return Err(self.err(format!(
7433 "expected ',' or ')' in INSERT column list, got {other:?}"
7434 )));
7435 }
7436 }
7437 }
7438 Some(names)
7439 } else {
7440 None
7441 };
7442 if matches!(self.peek(), Token::Select) {
7445 let select_stmt = match self.parse_select_stmt()? {
7446 Statement::Select(s) => s,
7447 other => {
7448 return Err(self.err(alloc::format!(
7449 "expected SELECT after INSERT INTO ... target, got {other:?}"
7450 )));
7451 }
7452 };
7453 let on_conflict = self.parse_optional_on_conflict()?;
7454 let returning = self.parse_optional_returning()?;
7455 return Ok(Statement::Insert(InsertStatement {
7456 ctes: Vec::new(),
7457 table,
7458 columns,
7459 rows: Vec::new(),
7460 select_source: Some(Box::new(select_stmt)),
7461 on_conflict,
7462 returning,
7463 }));
7464 }
7465 if !matches!(self.peek(), Token::Values) {
7466 return Err(self.err(format!(
7467 "expected VALUES or SELECT after table name, got {:?}",
7468 self.peek()
7469 )));
7470 }
7471 self.advance();
7472 if !matches!(self.peek(), Token::LParen) {
7473 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
7474 }
7475 let mut rows = Vec::new();
7476 loop {
7477 if !matches!(self.peek(), Token::LParen) {
7479 return Err(self.err(format!(
7480 "expected '(' for next VALUES tuple, got {:?}",
7481 self.peek()
7482 )));
7483 }
7484 self.advance();
7485 let mut tuple = Vec::new();
7486 loop {
7487 tuple.push(self.parse_expr(0)?);
7488 match self.peek() {
7489 Token::Comma => {
7490 self.advance();
7491 }
7492 Token::RParen => {
7493 self.advance();
7494 break;
7495 }
7496 other => {
7497 return Err(self.err(format!(
7498 "expected ',' or ')' in VALUES tuple, got {other:?}"
7499 )));
7500 }
7501 }
7502 }
7503 if tuple.is_empty() {
7504 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
7505 }
7506 rows.push(tuple);
7507 if matches!(self.peek(), Token::Comma) {
7509 self.advance();
7510 } else {
7511 break;
7512 }
7513 }
7514 let on_conflict = self.parse_optional_on_conflict()?;
7515 let returning = self.parse_optional_returning()?;
7516 Ok(Statement::Insert(InsertStatement {
7517 ctes: Vec::new(),
7518 table,
7519 columns,
7520 rows,
7521 select_source: None,
7522 on_conflict,
7523 returning,
7524 }))
7525 }
7526
7527 fn parse_optional_on_conflict(
7532 &mut self,
7533 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7534 if !matches!(self.peek(), Token::On) {
7535 return Ok(None);
7536 }
7537 let next_is_conflict = matches!(
7540 self.tokens.get(self.pos + 1),
7541 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7542 );
7543 if !next_is_conflict {
7544 return Ok(None);
7545 }
7546 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7550 if matches!(self.peek(), Token::LParen) {
7551 self.advance();
7552 loop {
7553 target_columns.push(self.expect_ident_like()?);
7554 match self.peek() {
7555 Token::Comma => {
7556 self.advance();
7557 }
7558 Token::RParen => {
7559 self.advance();
7560 break;
7561 }
7562 other => {
7563 return Err(self.err(alloc::format!(
7564 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7565 )));
7566 }
7567 }
7568 }
7569 }
7570 match self.advance() {
7572 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7573 other => {
7574 return Err(self.err(alloc::format!(
7575 "expected DO after ON CONFLICT [(…)], got {other:?}"
7576 )));
7577 }
7578 }
7579 let action = match self.advance() {
7581 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7582 crate::ast::OnConflictAction::Nothing
7583 }
7584 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7585 self.parse_on_conflict_update_action()?
7586 }
7587 other => {
7588 return Err(self.err(alloc::format!(
7589 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7590 )));
7591 }
7592 };
7593 Ok(Some(crate::ast::OnConflictClause {
7594 target_columns,
7595 action,
7596 }))
7597 }
7598
7599 fn parse_on_conflict_update_action(
7603 &mut self,
7604 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7605 match self.advance() {
7607 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7608 other => {
7609 return Err(self.err(alloc::format!(
7610 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7611 )));
7612 }
7613 }
7614 let mut assignments: Vec<(String, Expr)> = Vec::new();
7615 loop {
7616 let col = self.expect_ident_like()?;
7617 if !matches!(self.peek(), Token::Eq) {
7618 return Err(self.err(alloc::format!(
7619 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7620 self.peek()
7621 )));
7622 }
7623 self.advance();
7624 let value = self.parse_expr(0)?;
7625 assignments.push((col, value));
7626 if matches!(self.peek(), Token::Comma) {
7627 self.advance();
7628 continue;
7629 }
7630 break;
7631 }
7632 let where_ = if matches!(self.peek(), Token::Where) {
7633 self.advance();
7634 Some(self.parse_expr(0)?)
7635 } else {
7636 None
7637 };
7638 Ok(crate::ast::OnConflictAction::Update {
7639 assignments,
7640 where_,
7641 })
7642 }
7643
7644 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7645 let mut items = Vec::new();
7646 loop {
7647 items.push(self.parse_select_item()?);
7648 if matches!(self.peek(), Token::Comma) {
7649 self.advance();
7650 } else {
7651 break;
7652 }
7653 }
7654 Ok(items)
7655 }
7656
7657 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7658 if matches!(self.peek(), Token::Star) {
7659 self.advance();
7660 return Ok(SelectItem::Wildcard);
7661 }
7662 let expr = self.parse_expr(0)?;
7663 let alias = self.parse_optional_alias();
7664 Ok(SelectItem::Expr { expr, alias })
7665 }
7666
7667 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7668 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7678 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("jsonb_each_text"))
7679 && matches!(self.tokens.get(self.pos + 2), Some(Token::LParen))
7680 {
7681 self.advance(); self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7685 if !matches!(self.peek(), Token::RParen) {
7686 return Err(self.err(alloc::format!(
7687 "expected ')' after LATERAL jsonb_each_text() argument, got {:?}",
7688 self.peek()
7689 )));
7690 }
7691 self.advance();
7692 let (alias_ident, column_aliases) = self.parse_optional_alias_with_columns();
7693 let alias = alias_ident
7694 .clone()
7695 .unwrap_or_else(|| "jsonb_each_text".to_string());
7696 let srf_alias = "__srf__".to_string();
7702 let key_alias = column_aliases
7703 .first()
7704 .cloned()
7705 .unwrap_or_else(|| "key".to_string());
7706 let value_alias = column_aliases
7707 .get(1)
7708 .cloned()
7709 .unwrap_or_else(|| "value".to_string());
7710 let inner_select = crate::ast::SelectStatement {
7711 ctes: Vec::new(),
7712 distinct: false,
7713 items: alloc::vec![
7714 crate::ast::SelectItem::Expr {
7715 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7716 qualifier: Some(srf_alias.clone()),
7717 name: "key".to_string(),
7718 }),
7719 alias: Some(key_alias),
7720 },
7721 crate::ast::SelectItem::Expr {
7722 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7723 qualifier: Some(srf_alias.clone()),
7724 name: "value".to_string(),
7725 }),
7726 alias: Some(value_alias),
7727 },
7728 ],
7729 from: Some(crate::ast::FromClause {
7730 primary: TableRef {
7731 name: srf_alias.clone(),
7732 alias: Some(srf_alias.clone()),
7733 as_of_segment: None,
7734 unnest_expr: None,
7735 unnest_column_aliases: Vec::new(),
7736 generate_series_args: None,
7737 lateral_subquery: None,
7738 jsonb_each_text_arg: Some(Box::new(arg)),
7739 },
7740 joins: Vec::new(),
7741 }),
7742 where_: None,
7743 group_by: None,
7744 group_by_all: false,
7745 having: None,
7746 unions: Vec::new(),
7747 order_by: Vec::new(),
7748 limit: None,
7749 offset: None,
7750 limit_with_ties: false,
7751 };
7752 return Ok(TableRef {
7753 name: alias.clone(),
7754 alias: Some(alias),
7755 as_of_segment: None,
7756 unnest_expr: None,
7757 unnest_column_aliases: Vec::new(),
7758 generate_series_args: None,
7759 lateral_subquery: Some(Box::new(inner_select)),
7760 jsonb_each_text_arg: None,
7761 });
7762 }
7763 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7783 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7784 {
7785 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7789 Statement::Select(s) => s,
7790 other => {
7791 return Err(self.err(alloc::format!(
7792 "expected SELECT inside LATERAL ( … ), got {other:?}"
7793 )));
7794 }
7795 };
7796 if !matches!(self.peek(), Token::RParen) {
7797 return Err(self.err(alloc::format!(
7798 "expected ')' after LATERAL subquery, got {:?}",
7799 self.peek()
7800 )));
7801 }
7802 self.advance();
7803 let alias_ident = self.parse_optional_alias();
7804 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7805 return Ok(TableRef {
7806 name,
7807 alias: alias_ident,
7808 as_of_segment: None,
7809 unnest_expr: None,
7810 unnest_column_aliases: Vec::new(),
7811 generate_series_args: None,
7812 lateral_subquery: Some(Box::new(inner)),
7813 jsonb_each_text_arg: None,
7814 });
7815 }
7816 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("jsonb_each_text"))
7824 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7825 {
7826 self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7829 if !matches!(self.peek(), Token::RParen) {
7830 return Err(self.err(alloc::format!(
7831 "expected ')' after jsonb_each_text() argument, got {:?}",
7832 self.peek()
7833 )));
7834 }
7835 self.advance();
7836 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7837 let name = alias_ident
7838 .clone()
7839 .unwrap_or_else(|| "jsonb_each_text".to_string());
7840 return Ok(TableRef {
7841 name,
7842 alias: alias_ident,
7843 as_of_segment: None,
7844 unnest_expr: None,
7845 unnest_column_aliases: Vec::new(),
7846 generate_series_args: None,
7847 lateral_subquery: None,
7848 jsonb_each_text_arg: Some(Box::new(arg)),
7849 });
7850 }
7851 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
7855 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7856 {
7857 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
7860 if !matches!(self.peek(), Token::RParen) {
7861 return Err(self.err(alloc::format!(
7862 "expected ')' after unnest() argument, got {:?}",
7863 self.peek()
7864 )));
7865 }
7866 self.advance();
7867 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
7868 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
7869 return Ok(TableRef {
7870 name,
7871 alias: alias_ident,
7872 as_of_segment: None,
7873 unnest_expr: Some(Box::new(expr)),
7874 unnest_column_aliases,
7875 generate_series_args: None,
7876 lateral_subquery: None,
7877 jsonb_each_text_arg: None,
7878 });
7879 }
7880 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
7890 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7891 {
7892 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
7895 loop {
7896 args.push(self.parse_expr(0)?);
7897 if matches!(self.peek(), Token::Comma) {
7898 self.advance();
7899 continue;
7900 }
7901 break;
7902 }
7903 if !matches!(self.peek(), Token::RParen) {
7904 return Err(self.err(alloc::format!(
7905 "expected ')' after generate_series() arguments, got {:?}",
7906 self.peek()
7907 )));
7908 }
7909 self.advance();
7910 if args.len() < 2 || args.len() > 3 {
7911 return Err(self.err(alloc::format!(
7912 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
7913 args.len()
7914 )));
7915 }
7916 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7917 let name = alias_ident
7918 .clone()
7919 .unwrap_or_else(|| "generate_series".to_string());
7920 return Ok(TableRef {
7921 name,
7922 alias: alias_ident,
7923 as_of_segment: None,
7924 unnest_expr: None,
7925 unnest_column_aliases: Vec::new(),
7926 generate_series_args: Some(args),
7927 lateral_subquery: None,
7928 jsonb_each_text_arg: None,
7929 });
7930 }
7931 let name = if let Some(synth) = self.try_peek_meta_qualified() {
7940 synth
7941 } else if let Some(synth) = self.try_peek_meta_bare() {
7942 synth
7943 } else {
7944 self.expect_ident_like()?
7945 };
7946 let as_of_segment = if matches!(self.peek(), Token::As)
7952 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
7953 {
7954 self.advance(); self.advance(); let kw = match self.peek().clone() {
7957 Token::Ident(s) | Token::QuotedIdent(s) => s,
7958 other => {
7959 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
7960 }
7961 };
7962 if !kw.eq_ignore_ascii_case("segment") {
7963 return Err(self.err(format!(
7964 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
7965 )));
7966 }
7967 self.advance();
7968 let id = match self.advance() {
7971 Token::String(s) => s
7972 .parse::<u32>()
7973 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7974 Token::Integer(n) => u32::try_from(n)
7975 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7976 other => {
7977 return Err(self.err(format!(
7978 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7979 )));
7980 }
7981 };
7982 Some(id)
7983 } else {
7984 None
7985 };
7986 let alias = self.parse_optional_alias();
7987 Ok(TableRef {
7988 name,
7989 alias,
7990 as_of_segment,
7991 unnest_expr: None,
7992 unnest_column_aliases: Vec::new(),
7993 generate_series_args: None,
7994 lateral_subquery: None,
7995 jsonb_each_text_arg: None,
7996 })
7997 }
7998
7999 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
8005 let alias = self.parse_optional_alias();
8006 if alias.is_none() {
8007 return (None, Vec::new());
8008 }
8009 let mut cols: Vec<String> = Vec::new();
8010 if matches!(self.peek(), Token::LParen) {
8011 self.advance();
8012 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
8013 self.advance();
8014 cols.push(s);
8015 if matches!(self.peek(), Token::Comma) {
8016 self.advance();
8017 continue;
8018 }
8019 break;
8020 }
8021 if matches!(self.peek(), Token::RParen) {
8022 self.advance();
8023 }
8024 }
8025 (alias, cols)
8026 }
8027
8028 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
8033 let primary = self.parse_table_ref()?;
8034 let mut joins = Vec::new();
8035 loop {
8036 if matches!(self.peek(), Token::Comma) {
8038 self.advance();
8039 let table = self.parse_table_ref()?;
8040 joins.push(FromJoin {
8041 kind: JoinKind::Cross,
8042 table,
8043 on: None,
8044 });
8045 continue;
8046 }
8047 let kind =
8050 match self.peek() {
8051 Token::Inner => {
8052 self.advance();
8053 if !matches!(self.peek(), Token::Join) {
8054 return Err(self
8055 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
8056 }
8057 self.advance();
8058 JoinKind::Inner
8059 }
8060 Token::Left => {
8061 self.advance();
8062 if matches!(self.peek(), Token::Outer) {
8063 self.advance();
8064 }
8065 if !matches!(self.peek(), Token::Join) {
8066 return Err(self.err(format!(
8067 "expected JOIN after LEFT [OUTER], got {:?}",
8068 self.peek()
8069 )));
8070 }
8071 self.advance();
8072 JoinKind::Left
8073 }
8074 Token::Cross => {
8075 self.advance();
8076 if !matches!(self.peek(), Token::Join) {
8077 return Err(self
8078 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
8079 }
8080 self.advance();
8081 JoinKind::Cross
8082 }
8083 Token::Join => {
8084 self.advance();
8085 JoinKind::Inner
8086 }
8087 _ => break,
8088 };
8089 let table = self.parse_table_ref()?;
8090 let using_match = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using"));
8100 let on = if matches!(self.peek(), Token::On) {
8101 self.advance();
8102 Some(self.parse_expr(0)?)
8103 } else if using_match {
8104 self.advance();
8105 if !matches!(self.peek(), Token::LParen) {
8106 return Err(self.err(format!(
8107 "expected '(' after USING, got {:?}",
8108 self.peek()
8109 )));
8110 }
8111 self.advance();
8112 let mut cols: Vec<String> = Vec::new();
8113 loop {
8114 match self.peek().clone() {
8115 Token::Ident(s) | Token::QuotedIdent(s) => {
8116 self.advance();
8117 cols.push(s);
8118 }
8119 other => {
8120 return Err(self.err(format!(
8121 "expected column name inside USING (…), got {other:?}"
8122 )));
8123 }
8124 }
8125 match self.peek() {
8126 Token::Comma => {
8127 self.advance();
8128 continue;
8129 }
8130 Token::RParen => {
8131 self.advance();
8132 break;
8133 }
8134 other => {
8135 return Err(self.err(format!(
8136 "expected ',' or ')' inside USING (…), got {other:?}"
8137 )));
8138 }
8139 }
8140 }
8141 if cols.is_empty() {
8142 return Err(self.err("USING (…) requires at least one column".to_string()));
8143 }
8144 let left_qual: String = joins
8148 .last()
8149 .map(|j| {
8150 j.table
8151 .alias
8152 .clone()
8153 .unwrap_or_else(|| j.table.name.clone())
8154 })
8155 .unwrap_or_else(|| {
8156 primary
8157 .alias
8158 .clone()
8159 .unwrap_or_else(|| primary.name.clone())
8160 });
8161 let right_qual = table
8162 .alias
8163 .clone()
8164 .unwrap_or_else(|| table.name.clone());
8165 let mut iter = cols.into_iter().map(|c| Expr::Binary {
8166 lhs: alloc::boxed::Box::new(Expr::Column(crate::ast::ColumnName {
8167 qualifier: Some(left_qual.clone()),
8168 name: c.clone(),
8169 })),
8170 op: crate::ast::BinOp::Eq,
8171 rhs: alloc::boxed::Box::new(Expr::Column(crate::ast::ColumnName {
8172 qualifier: Some(right_qual.clone()),
8173 name: c,
8174 })),
8175 });
8176 let first = iter.next().expect("at least one col");
8177 Some(iter.fold(first, |acc, pred| Expr::Binary {
8178 lhs: alloc::boxed::Box::new(acc),
8179 op: crate::ast::BinOp::And,
8180 rhs: alloc::boxed::Box::new(pred),
8181 }))
8182 } else if kind == JoinKind::Cross {
8183 None
8184 } else {
8185 return Err(self.err(format!(
8186 "expected ON or USING after {:?} JOIN, got {:?}",
8187 kind,
8188 self.peek()
8189 )));
8190 };
8191 joins.push(FromJoin { kind, table, on });
8192 }
8193 Ok(FromClause { primary, joins })
8194 }
8195
8196 fn parse_optional_alias(&mut self) -> Option<String> {
8201 if matches!(self.peek(), Token::As) {
8202 self.advance();
8203 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
8208 return self.expect_ident_like().ok();
8209 }
8210 return None;
8211 }
8212 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8220 if is_alias_stopword(s) {
8221 return None;
8222 }
8223 return self.expect_ident_like().ok();
8224 }
8225 None
8226 }
8227
8228 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8230 self.enter_nested()?;
8234 let r = self.parse_expr_inner(min_prec);
8235 self.nest_depth -= 1;
8236 r
8237 }
8238
8239 fn parse_expr_inner(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8240 let mut lhs = self.parse_unary()?;
8241 let mut chain_len = 0usize;
8242 while let Some((op, prec)) = binop_from(self.peek()) {
8243 if prec < min_prec {
8244 break;
8245 }
8246 chain_len += 1;
8250 if chain_len > MAX_BINARY_CHAIN {
8251 return Err(self.err(alloc::format!(
8252 "more than {MAX_BINARY_CHAIN} chained binary operators; rewrite long OR-equality chains as IN (…)"
8253 )));
8254 }
8255 self.advance();
8256 let any_kind = match self.peek() {
8261 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
8262 Some(false)
8263 }
8264 Token::Ident(s) | Token::QuotedIdent(s)
8265 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
8266 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
8267 {
8268 Some(s.eq_ignore_ascii_case("any"))
8269 }
8270 _ => None,
8271 };
8272 if let Some(is_any) = any_kind {
8273 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
8276 if !matches!(self.peek(), Token::RParen) {
8277 return Err(self.err(alloc::format!(
8278 "expected ')' after ANY/ALL argument, got {:?}",
8279 self.peek()
8280 )));
8281 }
8282 self.advance();
8283 lhs = Expr::AnyAll {
8284 expr: Box::new(lhs),
8285 op,
8286 array: Box::new(arr),
8287 is_any,
8288 };
8289 continue;
8290 }
8291 let rhs = self.parse_expr(prec + 1)?;
8292 lhs = Expr::Binary {
8293 lhs: Box::new(lhs),
8294 op,
8295 rhs: Box::new(rhs),
8296 };
8297 }
8298 Ok(lhs)
8299 }
8300
8301 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
8302 match self.peek() {
8303 Token::Not => {
8304 self.advance();
8305 let e = self.parse_expr(3)?;
8308 Ok(Expr::Unary {
8309 op: UnOp::Not,
8310 expr: Box::new(e),
8311 })
8312 }
8313 Token::Minus => {
8314 self.advance();
8315 let e = self.parse_expr(8)?;
8318 Ok(Expr::Unary {
8319 op: UnOp::Neg,
8320 expr: Box::new(e),
8321 })
8322 }
8323 Token::Tilde => {
8324 self.advance();
8325 let e = self.parse_expr(8)?;
8327 Ok(Expr::Unary {
8328 op: UnOp::BitNot,
8329 expr: Box::new(e),
8330 })
8331 }
8332 _ => self.parse_atom(),
8333 }
8334 }
8335
8336 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
8337 let tok_pos = self.pos;
8338 match self.advance() {
8339 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
8340 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
8341 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
8342 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
8343 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
8344 Token::Null => Ok(Expr::Literal(Literal::Null)),
8345 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
8349 Token::LParen => {
8350 if matches!(self.peek(), Token::Select) {
8354 let inner = self.parse_select_stmt()?;
8355 match self.advance() {
8356 Token::RParen => {
8357 let Statement::Select(s) = inner else {
8358 unreachable!("parse_select_stmt returns Select")
8359 };
8360 Ok(Expr::ScalarSubquery(Box::new(s)))
8361 }
8362 other => Err(ParseError {
8363 message: format!("expected ')' after scalar subquery, got {other:?}"),
8364 token_pos: self.pos.saturating_sub(1),
8365 }),
8366 }
8367 } else {
8368 let e = self.parse_expr(0)?;
8369 match self.advance() {
8370 Token::RParen => Ok(e),
8371 other => Err(ParseError {
8372 message: format!("expected ')', got {other:?}"),
8373 token_pos: self.pos.saturating_sub(1),
8374 }),
8375 }
8376 }
8377 }
8378 Token::LBracket => self.parse_vector_literal_body(),
8379 Token::Extract => self.parse_extract_atom(),
8380 Token::Interval => self.parse_interval_atom(),
8381 Token::Left if matches!(self.peek(), Token::LParen) => {
8388 self.advance(); let mut args = Vec::new();
8390 if !matches!(self.peek(), Token::RParen) {
8391 loop {
8392 args.push(self.parse_expr(0)?);
8393 match self.peek() {
8394 Token::Comma => {
8395 self.advance();
8396 }
8397 Token::RParen => break,
8398 other => {
8399 return Err(self.err(alloc::format!(
8400 "expected ',' or ')' in left() args, got {other:?}"
8401 )));
8402 }
8403 }
8404 }
8405 }
8406 self.advance(); Ok(Expr::FunctionCall {
8408 name: "left".into(),
8409 args,
8410 })
8411 }
8412 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
8417 self.parse_exists_atom(false)
8418 }
8419 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
8423 self.parse_case_atom()
8424 }
8425 Token::Ident(s) | Token::QuotedIdent(s)
8429 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
8430 {
8431 self.advance(); let mut items: Vec<Expr> = Vec::new();
8433 if !matches!(self.peek(), Token::RBracket) {
8434 loop {
8435 items.push(self.parse_expr(0)?);
8436 match self.peek() {
8437 Token::Comma => {
8438 self.advance();
8439 }
8440 Token::RBracket => break,
8441 other => {
8442 return Err(self.err(alloc::format!(
8443 "expected ',' or ']' in ARRAY literal, got {other:?}"
8444 )));
8445 }
8446 }
8447 }
8448 }
8449 self.advance(); Ok(Expr::Array(items))
8451 }
8452 Token::Ident(s) | Token::QuotedIdent(s)
8467 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
8468 {
8469 self.parse_match_against_atom()
8470 }
8471 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
8472 other if unreserved_keyword_text(&other).is_some() => {
8479 let s = unreserved_keyword_text(&other).unwrap();
8480 self.finish_ident_atom(s)
8481 }
8482 other => Err(ParseError {
8483 message: format!("unexpected token {other:?} in expression"),
8484 token_pos: tok_pos,
8485 }),
8486 }
8487 .and_then(|atom| self.finish_postfix_casts(atom))
8489 }
8490
8491 fn parse_cast_target(&mut self) -> Result<CastTarget, ParseError> {
8496 let target = match self.advance() {
8497 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
8498 "int" | "integer" | "int4" => {
8499 if matches!(self.peek(), Token::LBracket)
8500 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8501 {
8502 self.advance();
8503 self.advance();
8504 CastTarget::IntArray
8505 } else {
8506 CastTarget::Int
8507 }
8508 }
8509 "bigint" | "int8" => {
8510 if matches!(self.peek(), Token::LBracket)
8511 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8512 {
8513 self.advance();
8514 self.advance();
8515 CastTarget::BigIntArray
8516 } else {
8517 CastTarget::BigInt
8518 }
8519 }
8520 "float" | "double" | "real" => CastTarget::Float,
8521 "text" => {
8522 if matches!(self.peek(), Token::LBracket)
8524 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8525 {
8526 self.advance();
8527 self.advance();
8528 CastTarget::TextArray
8529 } else {
8530 CastTarget::Text
8531 }
8532 }
8533 "bool" | "boolean" => CastTarget::Bool,
8534 "vector" => CastTarget::Vector,
8535 "date" => CastTarget::Date,
8536 "timestamp" | "datetime" => CastTarget::Timestamp,
8537 "timestamptz" => CastTarget::Timestamptz,
8538 "interval" => CastTarget::Interval,
8539 "json" => CastTarget::Json,
8540 "jsonb" => CastTarget::Jsonb,
8541 "regtype" => CastTarget::RegType,
8542 "regclass" => CastTarget::RegClass,
8543 "tsvector" => CastTarget::TsVector,
8547 "tsquery" => CastTarget::TsQuery,
8548 "uuid" => CastTarget::Uuid,
8551 "bytea" => CastTarget::Bytea,
8556 other => {
8565 let mut name = other.to_string();
8571 if matches!(self.peek(), Token::LParen) {
8572 let mut buf = alloc::string::String::from("(");
8573 let mut depth = 0usize;
8574 loop {
8575 match self.advance() {
8576 Token::LParen => {
8577 depth += 1;
8578 if depth > 1 {
8579 buf.push('(');
8580 }
8581 }
8582 Token::RParen => {
8583 depth -= 1;
8584 if depth == 0 {
8585 buf.push(')');
8586 break;
8587 }
8588 buf.push(')');
8589 }
8590 Token::Comma => buf.push(','),
8591 Token::Integer(n) => buf.push_str(&alloc::format!("{n}")),
8592 Token::Eof => break,
8593 _ => {}
8594 }
8595 }
8596 name.push_str(&buf);
8597 }
8598 if matches!(self.peek(), Token::LBracket)
8603 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8604 {
8605 self.advance();
8606 self.advance();
8607 name.push_str("_array");
8608 }
8609 CastTarget::Named(name)
8610 }
8611 },
8612 Token::Interval => CastTarget::Interval,
8613 other => {
8614 return Err(ParseError {
8615 message: format!("expected type ident after `::`, got {other:?}"),
8616 token_pos: self.pos.saturating_sub(1),
8617 });
8618 }
8619 };
8620 if matches!(self.peek(), Token::LBracket)
8629 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8630 {
8631 let widened = match &target {
8632 CastTarget::Bool => Some(CastTarget::Named("bool_array".to_string())),
8633 CastTarget::Date => Some(CastTarget::Named("date_array".to_string())),
8634 CastTarget::Timestamp | CastTarget::Timestamptz => {
8635 Some(CastTarget::Named("timestamptz_array".to_string()))
8636 }
8637 CastTarget::Uuid => Some(CastTarget::Named("uuid_array".to_string())),
8638 CastTarget::Json | CastTarget::Jsonb => {
8639 Some(CastTarget::Named("jsonb_array".to_string()))
8640 }
8641 CastTarget::Bytea => Some(CastTarget::Named("bytea_array".to_string())),
8642 CastTarget::Interval => Some(CastTarget::Named("interval_array".to_string())),
8643 CastTarget::Float => Some(CastTarget::Named("float_array".to_string())),
8644 CastTarget::Named(name) => {
8645 let mut a = name.clone();
8646 a.push_str("_array");
8647 Some(CastTarget::Named(a))
8648 }
8649 _ => None,
8653 };
8654 if let Some(w) = widened {
8655 self.advance();
8656 self.advance();
8657 return Ok(w);
8658 }
8659 }
8660 Ok(target)
8661 }
8662
8663 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
8664 loop {
8665 if matches!(self.peek(), Token::DoubleColon) {
8666 self.advance();
8667 let target = self.parse_cast_target()?;
8672 expr = Expr::Cast {
8673 expr: Box::new(expr),
8674 target,
8675 };
8676 continue;
8677 }
8678 if matches!(self.peek(), Token::Is) {
8679 self.advance();
8680 let negated = if matches!(self.peek(), Token::Not) {
8681 self.advance();
8682 true
8683 } else {
8684 false
8685 };
8686 if matches!(self.peek(), Token::Distinct) {
8689 self.advance();
8690 if !matches!(self.peek(), Token::From) {
8691 return Err(self.err(format!(
8692 "expected FROM after IS{} DISTINCT, got {:?}",
8693 if negated { " NOT" } else { "" },
8694 self.peek()
8695 )));
8696 }
8697 self.advance();
8698 let rhs = self.parse_expr(20)?;
8702 let op = if negated {
8703 BinOp::IsNotDistinctFrom
8704 } else {
8705 BinOp::IsDistinctFrom
8706 };
8707 expr = Expr::Binary {
8708 op,
8709 lhs: Box::new(expr),
8710 rhs: Box::new(rhs),
8711 };
8712 continue;
8713 }
8714 if !matches!(self.peek(), Token::Null) {
8715 return Err(self.err(format!(
8716 "expected NULL or DISTINCT after IS{}, got {:?}",
8717 if negated { " NOT" } else { "" },
8718 self.peek()
8719 )));
8720 }
8721 self.advance();
8722 expr = Expr::IsNull {
8723 expr: Box::new(expr),
8724 negated,
8725 };
8726 continue;
8727 }
8728 let negated = if matches!(self.peek(), Token::Not) {
8732 let next = self.tokens.get(self.pos + 1);
8733 matches!(next, Some(Token::Between | Token::In | Token::Like))
8734 || matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("ilike"))
8735 } else {
8736 false
8737 };
8738 if negated {
8739 self.advance();
8740 }
8741 if matches!(self.peek(), Token::Between) {
8742 expr = self.parse_between_tail(expr, negated)?;
8743 continue;
8744 }
8745 if matches!(self.peek(), Token::In) {
8746 expr = self.parse_in_tail(expr, negated)?;
8747 continue;
8748 }
8749 if matches!(self.peek(), Token::Like) {
8750 self.advance();
8751 let pattern = self.parse_expr(5)?;
8754 expr = Expr::Like {
8755 expr: Box::new(expr),
8756 pattern: Box::new(pattern),
8757 negated,
8758 case_insensitive: false,
8759 };
8760 continue;
8761 }
8762 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("ilike")) {
8765 self.advance();
8766 let pattern = self.parse_expr(5)?;
8767 expr = Expr::Like {
8768 expr: Box::new(expr),
8769 pattern: Box::new(pattern),
8770 negated,
8771 case_insensitive: true,
8772 };
8773 continue;
8774 }
8775 if matches!(self.peek(), Token::LBracket) {
8779 self.advance();
8780 let index = self.parse_expr(0)?;
8781 if !matches!(self.peek(), Token::RBracket) {
8782 return Err(self.err(alloc::format!(
8783 "expected ']' after array index, got {:?}",
8784 self.peek()
8785 )));
8786 }
8787 self.advance();
8788 expr = Expr::ArraySubscript {
8789 target: Box::new(expr),
8790 index: Box::new(index),
8791 };
8792 continue;
8793 }
8794 return Ok(expr);
8795 }
8796 }
8797
8798 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8802 self.advance(); let low = self.parse_expr(5)?;
8804 if !matches!(self.peek(), Token::And) {
8805 return Err(self.err(format!(
8806 "expected AND after BETWEEN low bound, got {:?}",
8807 self.peek()
8808 )));
8809 }
8810 self.advance();
8811 let high = self.parse_expr(5)?;
8812 let target = Box::new(expr);
8813 let combined = Expr::Binary {
8814 lhs: Box::new(Expr::Binary {
8815 lhs: target.clone(),
8816 op: BinOp::GtEq,
8817 rhs: Box::new(low),
8818 }),
8819 op: BinOp::And,
8820 rhs: Box::new(Expr::Binary {
8821 lhs: target,
8822 op: BinOp::LtEq,
8823 rhs: Box::new(high),
8824 }),
8825 };
8826 Ok(maybe_not(combined, negated))
8827 }
8828
8829 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
8834 let mut recursive = false;
8839 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8840 && s.eq_ignore_ascii_case("recursive")
8841 {
8842 self.advance();
8843 recursive = true;
8844 }
8845 let mut ctes = Vec::new();
8846 loop {
8847 let name = self.expect_ident_like()?;
8848 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
8852 self.advance();
8853 let mut names = Vec::new();
8854 loop {
8855 names.push(self.expect_ident_like()?);
8856 if matches!(self.peek(), Token::Comma) {
8857 self.advance();
8858 continue;
8859 }
8860 break;
8861 }
8862 if !matches!(self.peek(), Token::RParen) {
8863 return Err(self.err(format!(
8864 "expected ')' to close CTE column list, got {:?}",
8865 self.peek()
8866 )));
8867 }
8868 self.advance();
8869 names
8870 } else {
8871 Vec::new()
8872 };
8873 if !matches!(self.peek(), Token::As) {
8877 return Err(self.err(format!(
8878 "expected AS after CTE name {name:?}, got {:?}",
8879 self.peek()
8880 )));
8881 }
8882 self.advance();
8883 if !matches!(self.peek(), Token::LParen) {
8884 return Err(self.err(format!(
8885 "expected '(' after AS in WITH clause, got {:?}",
8886 self.peek()
8887 )));
8888 }
8889 self.advance();
8890 let is_update_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
8897 let is_delete_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
8898 let body = match self.peek() {
8899 Token::Select => {
8900 let inner = self.parse_select_stmt()?;
8901 let Statement::Select(s) = inner else {
8902 unreachable!("parse_select_stmt returns Select");
8903 };
8904 crate::ast::CteBody::Select(s)
8905 }
8906 Token::Insert => {
8907 let inner = self.parse_one_statement()?;
8908 let Statement::Insert(s) = inner else {
8909 unreachable!("Token::Insert routes to Insert");
8910 };
8911 crate::ast::CteBody::Insert(alloc::boxed::Box::new(s))
8912 }
8913 _ if is_update_kw => {
8914 let inner = self.parse_one_statement()?;
8915 let Statement::Update(s) = inner else {
8916 return Err(
8917 self.err(format!("expected UPDATE inside WITH (…), got {inner:?}"))
8918 );
8919 };
8920 crate::ast::CteBody::Update(alloc::boxed::Box::new(s))
8921 }
8922 _ if is_delete_kw => {
8923 let inner = self.parse_one_statement()?;
8924 let Statement::Delete(s) = inner else {
8925 return Err(
8926 self.err(format!("expected DELETE inside WITH (…), got {inner:?}"))
8927 );
8928 };
8929 crate::ast::CteBody::Delete(alloc::boxed::Box::new(s))
8930 }
8931 other => {
8932 return Err(self.err(format!(
8933 "WITH body must be SELECT / INSERT / UPDATE / DELETE, got {other:?}"
8934 )));
8935 }
8936 };
8937 if !matches!(self.peek(), Token::RParen) {
8938 return Err(self.err(format!(
8939 "expected ')' after CTE body, got {:?}",
8940 self.peek()
8941 )));
8942 }
8943 self.advance();
8944 ctes.push(crate::ast::Cte {
8945 name,
8946 body,
8947 recursive,
8948 column_overrides,
8949 });
8950 if matches!(self.peek(), Token::Comma) {
8951 self.advance();
8952 continue;
8953 }
8954 break;
8955 }
8956 let outer_is_update = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
8960 let outer_is_delete = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
8961 match self.peek() {
8962 Token::Select => {
8963 let body_stmt = self.parse_select_stmt()?;
8964 let Statement::Select(mut body) = body_stmt else {
8965 unreachable!()
8966 };
8967 body.ctes = ctes;
8968 Ok(Statement::Select(body))
8969 }
8970 Token::Insert => {
8971 let body_stmt = self.parse_one_statement()?;
8972 let Statement::Insert(mut body) = body_stmt else {
8973 unreachable!()
8974 };
8975 body.ctes = ctes;
8976 Ok(Statement::Insert(body))
8977 }
8978 _ if outer_is_update => {
8979 let body_stmt = self.parse_one_statement()?;
8980 let Statement::Update(mut body) = body_stmt else {
8981 return Err(self.err(format!("expected UPDATE after WITH clause")));
8982 };
8983 body.ctes = ctes;
8984 Ok(Statement::Update(body))
8985 }
8986 _ if outer_is_delete => {
8987 let body_stmt = self.parse_one_statement()?;
8988 let Statement::Delete(mut body) = body_stmt else {
8989 return Err(self.err(format!("expected DELETE after WITH clause")));
8990 };
8991 body.ctes = ctes;
8992 Ok(Statement::Delete(body))
8993 }
8994 other => Err(self.err(format!(
8995 "expected SELECT / INSERT / UPDATE / DELETE after WITH clause, got {other:?}"
8996 ))),
8997 }
8998 }
8999
9000 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
9009 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
9013 None
9014 } else {
9015 Some(Box::new(self.parse_expr(0)?))
9016 };
9017 let mut branches: Vec<(Expr, Expr)> = Vec::new();
9018 loop {
9019 match self.peek() {
9020 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
9021 self.advance();
9022 let cond = self.parse_expr(0)?;
9023 match self.peek() {
9024 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
9025 self.advance();
9026 }
9027 other => {
9028 return Err(self.err(alloc::format!(
9029 "expected THEN after CASE WHEN <expr>, got {other:?}"
9030 )));
9031 }
9032 }
9033 let value = self.parse_expr(0)?;
9034 branches.push((cond, value));
9035 }
9036 _ => break,
9037 }
9038 }
9039 if branches.is_empty() {
9040 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
9041 }
9042 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
9043 {
9044 self.advance();
9045 Some(Box::new(self.parse_expr(0)?))
9046 } else {
9047 None
9048 };
9049 match self.peek() {
9050 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
9051 self.advance();
9052 }
9053 other => {
9054 return Err(self.err(alloc::format!(
9055 "expected END to close CASE expression, got {other:?}"
9056 )));
9057 }
9058 }
9059 Ok(Expr::Case {
9060 operand,
9061 branches,
9062 else_branch,
9063 })
9064 }
9065
9066 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
9067 if !matches!(self.peek(), Token::LParen) {
9068 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
9069 }
9070 self.advance();
9071 let inner = self.parse_select_stmt()?;
9072 if !matches!(self.peek(), Token::RParen) {
9073 return Err(self.err(format!(
9074 "expected ')' after EXISTS-subquery, got {:?}",
9075 self.peek()
9076 )));
9077 }
9078 self.advance();
9079 let Statement::Select(s) = inner else {
9080 unreachable!("parse_select_stmt returns Select")
9081 };
9082 Ok(Expr::Exists {
9083 subquery: Box::new(s),
9084 negated,
9085 })
9086 }
9087
9088 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
9089 self.advance(); if !matches!(self.peek(), Token::LParen) {
9091 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
9092 }
9093 self.advance();
9094 if matches!(self.peek(), Token::Select) {
9096 let inner = self.parse_select_stmt()?;
9097 if !matches!(self.peek(), Token::RParen) {
9098 return Err(self.err(format!(
9099 "expected ')' after IN-subquery, got {:?}",
9100 self.peek()
9101 )));
9102 }
9103 self.advance();
9104 let Statement::Select(s) = inner else {
9105 unreachable!("parse_select_stmt always returns Statement::Select")
9106 };
9107 return Ok(Expr::InSubquery {
9108 expr: Box::new(expr),
9109 subquery: Box::new(s),
9110 negated,
9111 });
9112 }
9113 let mut elements = Vec::new();
9114 if !matches!(self.peek(), Token::RParen) {
9115 loop {
9116 elements.push(self.parse_expr(0)?);
9117 match self.peek() {
9118 Token::Comma => {
9119 self.advance();
9120 }
9121 Token::RParen => break,
9122 other => {
9123 return Err(
9124 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
9125 );
9126 }
9127 }
9128 }
9129 }
9130 self.advance(); if elements.is_empty() {
9135 return Ok(maybe_not(Expr::Literal(Literal::Bool(false)), negated));
9136 }
9137 Ok(Expr::InList {
9138 expr: Box::new(expr),
9139 list: elements,
9140 negated,
9141 })
9142 }
9143
9144 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
9165 if !matches!(self.peek(), Token::LParen) {
9168 return Err(self.err(alloc::format!(
9169 "expected '(' after MATCH, got {:?}",
9170 self.peek()
9171 )));
9172 }
9173 self.advance();
9174 let mut cols: Vec<Expr> = Vec::new();
9175 loop {
9176 cols.push(self.parse_expr(0)?);
9177 match self.peek() {
9178 Token::Comma => {
9179 self.advance();
9180 }
9181 Token::RParen => break,
9182 other => {
9183 return Err(self.err(alloc::format!(
9184 "expected ',' or ')' in MATCH column list, got {other:?}"
9185 )));
9186 }
9187 }
9188 }
9189 self.advance(); match self.peek() {
9192 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
9193 self.advance();
9194 }
9195 other => {
9196 return Err(self.err(alloc::format!(
9197 "expected AGAINST after MATCH column list, got {other:?}"
9198 )));
9199 }
9200 }
9201 if !matches!(self.peek(), Token::LParen) {
9202 return Err(self.err(alloc::format!(
9203 "expected '(' after AGAINST, got {:?}",
9204 self.peek()
9205 )));
9206 }
9207 self.advance();
9208 let term = match self.advance() {
9218 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
9219 Token::Placeholder(n) => Expr::Placeholder(n),
9220 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
9221 qualifier: None,
9222 name: s,
9223 }),
9224 other => {
9225 return Err(self.err(alloc::format!(
9226 "MATCH ... AGAINST(<term>) expects a string literal, \
9227 bound parameter, or column ref, got {other:?}"
9228 )));
9229 }
9230 };
9231 loop {
9236 match self.peek() {
9237 Token::In => {
9240 self.advance();
9241 }
9242 Token::Ident(s) | Token::QuotedIdent(s)
9243 if s.eq_ignore_ascii_case("natural")
9244 || s.eq_ignore_ascii_case("language")
9245 || s.eq_ignore_ascii_case("boolean")
9246 || s.eq_ignore_ascii_case("mode")
9247 || s.eq_ignore_ascii_case("with")
9248 || s.eq_ignore_ascii_case("query")
9249 || s.eq_ignore_ascii_case("expansion") =>
9250 {
9251 self.advance();
9252 }
9253 _ => break,
9254 }
9255 }
9256 if !matches!(self.peek(), Token::RParen) {
9257 return Err(self.err(alloc::format!(
9258 "expected ')' to close AGAINST, got {:?}",
9259 self.peek()
9260 )));
9261 }
9262 self.advance();
9263 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
9266 let plainto = Expr::FunctionCall {
9267 name: String::from("plainto_tsquery"),
9268 args: alloc::vec![simple_lit(), term.clone()],
9269 };
9270 let mut folded: Option<Expr> = None;
9271 for col in cols {
9272 let to_tsv = Expr::FunctionCall {
9273 name: String::from("to_tsvector"),
9274 args: alloc::vec![simple_lit(), col],
9275 };
9276 let leaf = Expr::Binary {
9277 lhs: Box::new(to_tsv),
9278 op: crate::ast::BinOp::TsMatch,
9279 rhs: Box::new(plainto.clone()),
9280 };
9281 folded = Some(match folded {
9282 None => leaf,
9283 Some(prev) => Expr::Binary {
9284 lhs: Box::new(prev),
9285 op: crate::ast::BinOp::Or,
9286 rhs: Box::new(leaf),
9287 },
9288 });
9289 }
9290 match folded {
9291 Some(e) => Ok(e),
9292 None => Err(self.err(String::from(
9293 "MATCH(...) AGAINST(...) requires at least one column",
9294 ))),
9295 }
9296 }
9297
9298 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
9299 if !matches!(self.peek(), Token::LParen) {
9300 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
9301 }
9302 self.advance();
9303 let field_name = self.expect_ident_like()?;
9304 let field = match field_name.to_ascii_lowercase().as_str() {
9305 "year" => ExtractField::Year,
9306 "month" => ExtractField::Month,
9307 "day" => ExtractField::Day,
9308 "hour" => ExtractField::Hour,
9309 "minute" => ExtractField::Minute,
9310 "second" => ExtractField::Second,
9311 "microsecond" | "microseconds" => ExtractField::Microsecond,
9312 "epoch" => ExtractField::Epoch,
9313 other => {
9314 return Err(self.err(format!(
9315 "unknown EXTRACT field {other:?}; \
9316 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
9317 )));
9318 }
9319 };
9320 if !matches!(self.peek(), Token::From) {
9321 return Err(self.err(format!(
9322 "expected FROM after EXTRACT field, got {:?}",
9323 self.peek()
9324 )));
9325 }
9326 self.advance();
9327 let source = self.parse_expr(0)?;
9328 if !matches!(self.peek(), Token::RParen) {
9329 return Err(self.err(format!(
9330 "expected ')' to close EXTRACT, got {:?}",
9331 self.peek()
9332 )));
9333 }
9334 self.advance();
9335 Ok(Expr::Extract {
9336 field,
9337 source: Box::new(source),
9338 })
9339 }
9340
9341 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
9346 let tok = self.advance();
9347 let Token::String(text) = tok else {
9348 return Err(self.err(format!(
9349 "expected string literal after INTERVAL, got {tok:?}"
9350 )));
9351 };
9352 let (months, days, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
9353 message: format!(
9354 "cannot parse INTERVAL {text:?}; \
9355 expected `<n> <unit> [<n> <unit> ...]` with units \
9356 microsecond[s], millisecond[s], second[s], minute[s], \
9357 hour[s], day[s], week[s], month[s], year[s]"
9358 ),
9359 token_pos: self.pos.saturating_sub(1),
9360 })?;
9361 Ok(Expr::Literal(Literal::Interval {
9362 months,
9363 days,
9364 micros,
9365 text,
9366 }))
9367 }
9368
9369 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
9370 let mut elems = Vec::new();
9371 if matches!(self.peek(), Token::RBracket) {
9372 self.advance();
9373 return Ok(Expr::Literal(Literal::Vector(elems)));
9374 }
9375 loop {
9376 let e = self.parse_expr(0)?;
9377 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
9378 message: format!("vector element must be a numeric literal, got {e:?}"),
9379 token_pos: self.pos,
9380 })?;
9381 elems.push(x);
9382 match self.peek() {
9383 Token::Comma => {
9384 self.advance();
9385 }
9386 Token::RBracket => {
9387 self.advance();
9388 break;
9389 }
9390 other => {
9391 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
9392 }
9393 }
9394 }
9395 Ok(Expr::Literal(Literal::Vector(elems)))
9396 }
9397
9398 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
9407 let Token::Ident(s) = self.peek().clone() else {
9408 return NullTreatment::Respect;
9409 };
9410 let is_ignore = s.eq_ignore_ascii_case("ignore");
9411 let is_respect = s.eq_ignore_ascii_case("respect");
9412 if !is_ignore && !is_respect {
9413 return NullTreatment::Respect;
9414 }
9415 if self.pos + 1 < self.tokens.len()
9418 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
9419 && s2.eq_ignore_ascii_case("nulls")
9420 {
9421 self.advance();
9422 self.advance();
9423 return if is_ignore {
9424 NullTreatment::Ignore
9425 } else {
9426 NullTreatment::Respect
9427 };
9428 }
9429 NullTreatment::Respect
9430 }
9431
9432 fn parse_filter_clause(&mut self) -> Result<Option<Box<Expr>>, ParseError> {
9437 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9438 return Ok(None);
9439 };
9440 if !s.eq_ignore_ascii_case("filter") {
9441 return Ok(None);
9442 }
9443 self.advance(); if !matches!(self.peek(), Token::LParen) {
9445 return Err(self.err(format!("expected '(' after FILTER, got {:?}", self.peek())));
9446 }
9447 self.advance(); if !matches!(self.peek(), Token::Where) {
9449 return Err(self.err(format!(
9450 "expected WHERE inside FILTER (...), got {:?}",
9451 self.peek()
9452 )));
9453 }
9454 self.advance(); let cond = self.parse_expr(0)?;
9456 if !matches!(self.peek(), Token::RParen) {
9457 return Err(self.err(format!(
9458 "expected ')' to close FILTER (WHERE ...), got {:?}",
9459 self.peek()
9460 )));
9461 }
9462 self.advance(); Ok(Some(Box::new(cond)))
9464 }
9465
9466 fn parse_within_group_clause(&mut self) -> Result<Vec<OrderBy>, ParseError> {
9471 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9472 return Ok(Vec::new());
9473 };
9474 if !s.eq_ignore_ascii_case("within") {
9475 return Ok(Vec::new());
9476 }
9477 self.advance(); if !matches!(self.peek(), Token::Group) {
9479 return Err(self.err(format!(
9480 "expected GROUP after WITHIN, got {:?}",
9481 self.peek()
9482 )));
9483 }
9484 self.advance(); if !matches!(self.peek(), Token::LParen) {
9486 return Err(self.err(format!(
9487 "expected '(' after WITHIN GROUP, got {:?}",
9488 self.peek()
9489 )));
9490 }
9491 self.advance(); if !matches!(self.peek(), Token::Order) {
9493 return Err(self.err(format!(
9494 "expected ORDER BY inside WITHIN GROUP (...), got {:?}",
9495 self.peek()
9496 )));
9497 }
9498 self.advance(); if !matches!(self.peek(), Token::By) {
9500 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9501 }
9502 self.advance(); let mut keys: Vec<OrderBy> = Vec::new();
9504 loop {
9505 let expr = self.parse_expr(0)?;
9506 let desc = if matches!(self.peek(), Token::Desc) {
9507 self.advance();
9508 true
9509 } else if matches!(self.peek(), Token::Asc) {
9510 self.advance();
9511 false
9512 } else {
9513 false
9514 };
9515 let nulls_first = self.parse_optional_nulls_placement()?;
9516 keys.push(OrderBy {
9517 expr,
9518 desc,
9519 nulls_first,
9520 });
9521 if matches!(self.peek(), Token::Comma) {
9522 self.advance();
9523 } else {
9524 break;
9525 }
9526 }
9527 if !matches!(self.peek(), Token::RParen) {
9528 return Err(self.err(format!(
9529 "expected ')' to close WITHIN GROUP (ORDER BY ...), got {:?}",
9530 self.peek()
9531 )));
9532 }
9533 self.advance(); Ok(keys)
9535 }
9536
9537 #[allow(clippy::type_complexity)] fn parse_over_clause(
9540 &mut self,
9541 ) -> Result<
9542 (
9543 Vec<Expr>,
9544 Vec<(Expr, bool, Option<bool>)>,
9545 Option<WindowFrame>,
9546 ),
9547 ParseError,
9548 > {
9549 if !matches!(self.peek(), Token::LParen) {
9550 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
9551 }
9552 self.advance();
9553 let mut partition_by = Vec::new();
9554 let mut order_by = Vec::new();
9555 let is_partition_kw = match self.peek() {
9561 Token::Partition => true,
9562 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("partition"),
9563 _ => false,
9564 };
9565 if is_partition_kw {
9566 self.advance();
9567 if !matches!(self.peek(), Token::By) {
9568 return Err(self.err(format!(
9569 "expected BY after PARTITION, got {:?}",
9570 self.peek()
9571 )));
9572 }
9573 self.advance();
9574 loop {
9575 partition_by.push(self.parse_expr(0)?);
9576 if matches!(self.peek(), Token::Comma) {
9577 self.advance();
9578 continue;
9579 }
9580 break;
9581 }
9582 }
9583 if matches!(self.peek(), Token::Order) {
9585 self.advance();
9586 if !matches!(self.peek(), Token::By) {
9587 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9588 }
9589 self.advance();
9590 loop {
9591 let e = self.parse_expr(0)?;
9592 let desc = if matches!(self.peek(), Token::Desc) {
9593 self.advance();
9594 true
9595 } else if matches!(self.peek(), Token::Asc) {
9596 self.advance();
9597 false
9598 } else {
9599 false
9600 };
9601 let nulls_first = self.parse_optional_nulls_placement()?;
9603 order_by.push((e, desc, nulls_first));
9604 if matches!(self.peek(), Token::Comma) {
9605 self.advance();
9606 continue;
9607 }
9608 break;
9609 }
9610 }
9611 let mut frame: Option<WindowFrame> = None;
9615 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
9616 let kind = if s.eq_ignore_ascii_case("rows") {
9617 Some(FrameKind::Rows)
9618 } else if s.eq_ignore_ascii_case("range") {
9619 Some(FrameKind::Range)
9620 } else {
9621 None
9622 };
9623 if let Some(kind) = kind {
9624 self.advance();
9625 frame = Some(self.parse_frame_tail(kind)?);
9626 }
9627 }
9628 if !matches!(self.peek(), Token::RParen) {
9629 return Err(self.err(format!(
9630 "expected ')' to close OVER clause, got {:?}",
9631 self.peek()
9632 )));
9633 }
9634 self.advance();
9635 Ok((partition_by, order_by, frame))
9636 }
9637
9638 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
9644 if matches!(self.peek(), Token::Between) {
9645 self.advance();
9646 let start = self.parse_frame_bound()?;
9647 if !matches!(self.peek(), Token::And) {
9648 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
9649 }
9650 self.advance();
9651 let end = self.parse_frame_bound()?;
9652 Ok(WindowFrame {
9653 kind,
9654 start,
9655 end: Some(end),
9656 })
9657 } else {
9658 let start = self.parse_frame_bound()?;
9659 Ok(WindowFrame {
9660 kind,
9661 start,
9662 end: None,
9663 })
9664 }
9665 }
9666
9667 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
9670 if let Token::Integer(n) = *self.peek() {
9672 self.advance();
9673 let n: u64 = u64::try_from(n).map_err(|_| {
9674 self.err(format!(
9675 "invalid frame offset {n} — expected non-negative integer"
9676 ))
9677 })?;
9678 let dir = self.expect_ident_like()?;
9679 return if dir.eq_ignore_ascii_case("preceding") {
9680 Ok(FrameBound::OffsetPreceding(n))
9681 } else if dir.eq_ignore_ascii_case("following") {
9682 Ok(FrameBound::OffsetFollowing(n))
9683 } else {
9684 Err(self.err(format!(
9685 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
9686 )))
9687 };
9688 }
9689 let first = self.expect_ident_like()?;
9690 if first.eq_ignore_ascii_case("unbounded") {
9691 let dir = self.expect_ident_like()?;
9692 return if dir.eq_ignore_ascii_case("preceding") {
9693 Ok(FrameBound::UnboundedPreceding)
9694 } else if dir.eq_ignore_ascii_case("following") {
9695 Ok(FrameBound::UnboundedFollowing)
9696 } else {
9697 Err(self.err(format!(
9698 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
9699 )))
9700 };
9701 }
9702 if first.eq_ignore_ascii_case("current") {
9703 let row = self.expect_ident_like()?;
9704 if !row.eq_ignore_ascii_case("row") {
9705 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
9706 }
9707 return Ok(FrameBound::CurrentRow);
9708 }
9709 Err(self.err(format!(
9710 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
9711 )))
9712 }
9713
9714 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
9715 if matches!(self.peek(), Token::Dot) {
9716 self.advance();
9717 let name = self.expect_ident_like()?;
9718 if matches!(self.peek(), Token::LParen) {
9724 return self.finish_ident_atom(name);
9725 }
9726 return Ok(Expr::Column(ColumnName {
9727 qualifier: Some(first),
9728 name,
9729 }));
9730 }
9731 if matches!(self.peek(), Token::LParen) {
9732 self.advance();
9733 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
9737 self.advance();
9738 if !matches!(self.peek(), Token::RParen) {
9739 return Err(self.err(format!(
9740 "expected ')' after COUNT(*), got {:?}",
9741 self.peek()
9742 )));
9743 }
9744 self.advance();
9745 let filter = self.parse_filter_clause()?;
9747 let null_treatment = self.parse_null_treatment_modifier();
9749 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9750 && s.eq_ignore_ascii_case("over")
9751 {
9752 if filter.is_some() {
9753 return Err(
9754 self.err("FILTER on window functions is not supported yet".into())
9755 );
9756 }
9757 self.advance();
9758 let (partition_by, order_by, frame) = self.parse_over_clause()?;
9759 return Ok(Expr::WindowFunction {
9760 name: "count_star".into(),
9761 args: Vec::new(),
9762 partition_by,
9763 order_by,
9764 frame,
9765 null_treatment,
9766 });
9767 }
9768 if let Some(filter) = filter {
9769 return Ok(Expr::AggregateOrdered {
9770 call: Box::new(Expr::FunctionCall {
9771 name: "count_star".into(),
9772 args: Vec::new(),
9773 }),
9774 order_by: Vec::new(),
9775 distinct: false,
9776 filter: Some(filter),
9777 });
9778 }
9779 return Ok(Expr::FunctionCall {
9780 name: "count_star".into(),
9781 args: Vec::new(),
9782 });
9783 }
9784 let mut args = Vec::new();
9786 let mut agg_order_by: Vec<OrderBy> = Vec::new();
9787 let agg_distinct = if matches!(self.peek(), Token::Distinct) {
9791 self.advance();
9792 true
9793 } else if matches!(self.peek(), Token::All) {
9794 self.advance();
9795 false
9796 } else {
9797 false
9798 };
9799 if !matches!(self.peek(), Token::RParen) {
9800 loop {
9801 args.push(self.parse_expr(0)?);
9802 if first.eq_ignore_ascii_case("cast")
9806 && args.len() == 1
9807 && matches!(self.peek(), Token::As)
9808 {
9809 self.advance();
9810 let target = self.parse_cast_target()?;
9811 if !matches!(self.peek(), Token::RParen) {
9812 return Err(self.err(format!(
9813 "expected ')' to close CAST, got {:?}",
9814 self.peek()
9815 )));
9816 }
9817 self.advance();
9818 return Ok(Expr::Cast {
9819 expr: Box::new(args.pop().expect("one arg")),
9820 target,
9821 });
9822 }
9823 if (first.eq_ignore_ascii_case("substring")
9829 || first.eq_ignore_ascii_case("substr"))
9830 && args.len() == 1
9831 && matches!(self.peek(), Token::From)
9832 {
9833 self.advance();
9834 let start = self.parse_expr(0)?;
9835 args.push(start);
9836 if matches!(self.peek(), Token::For) {
9837 self.advance();
9838 let length = self.parse_expr(0)?;
9839 args.push(length);
9840 }
9841 if !matches!(self.peek(), Token::RParen) {
9842 return Err(self.err(format!(
9843 "expected ')' to close substring(... FROM ... [FOR ...]), got {:?}",
9844 self.peek()
9845 )));
9846 }
9847 self.advance();
9848 return Ok(Expr::FunctionCall {
9849 name: first.to_ascii_lowercase(),
9850 args,
9851 });
9852 }
9853 if matches!(self.peek(), Token::Order) {
9857 self.advance();
9858 if !matches!(self.peek(), Token::By) {
9859 return Err(self.err(format!(
9860 "expected BY after ORDER in aggregate args, got {:?}",
9861 self.peek()
9862 )));
9863 }
9864 self.advance();
9865 loop {
9866 let expr = self.parse_expr(0)?;
9867 let desc = if matches!(self.peek(), Token::Desc) {
9868 self.advance();
9869 true
9870 } else if matches!(self.peek(), Token::Asc) {
9871 self.advance();
9872 false
9873 } else {
9874 false
9875 };
9876 let nulls_first = self.parse_optional_nulls_placement()?;
9877 agg_order_by.push(OrderBy {
9878 expr,
9879 desc,
9880 nulls_first,
9881 });
9882 if matches!(self.peek(), Token::Comma) {
9883 self.advance();
9884 } else {
9885 break;
9886 }
9887 }
9888 if !matches!(self.peek(), Token::RParen) {
9889 return Err(self.err(format!(
9890 "expected ')' after aggregate ORDER BY, got {:?}",
9891 self.peek()
9892 )));
9893 }
9894 break;
9895 }
9896 match self.peek() {
9897 Token::Comma => {
9898 self.advance();
9899 }
9900 Token::RParen => break,
9901 other => {
9902 return Err(self.err(format!(
9903 "expected ',' or ')' in function args, got {other:?}"
9904 )));
9905 }
9906 }
9907 }
9908 }
9909 self.advance(); let within_group_order = self.parse_within_group_clause()?;
9918 if !within_group_order.is_empty() && !agg_order_by.is_empty() {
9919 return Err(self.err(
9920 "an aggregate may not carry both an in-argument ORDER BY and WITHIN GROUP"
9921 .into(),
9922 ));
9923 }
9924 let agg_order_by = if within_group_order.is_empty() {
9925 agg_order_by
9926 } else {
9927 within_group_order
9928 };
9929 let filter = self.parse_filter_clause()?;
9931 let null_treatment = self.parse_null_treatment_modifier();
9938 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9939 && s.eq_ignore_ascii_case("over")
9940 {
9941 if filter.is_some() {
9942 return Err(self.err("FILTER on window functions is not supported yet".into()));
9943 }
9944 self.advance();
9945 let (partition_by, order_by, frame) = self.parse_over_clause()?;
9946 return Ok(Expr::WindowFunction {
9947 name: first,
9948 args,
9949 partition_by,
9950 order_by,
9951 frame,
9952 null_treatment,
9953 });
9954 }
9955 if !agg_order_by.is_empty() || agg_distinct || filter.is_some() {
9956 return Ok(Expr::AggregateOrdered {
9957 call: Box::new(Expr::FunctionCall { name: first, args }),
9958 order_by: agg_order_by,
9959 distinct: agg_distinct,
9960 filter,
9961 });
9962 }
9963 return Ok(Expr::FunctionCall { name: first, args });
9964 }
9965 let lc = first.to_ascii_lowercase();
9971 if matches!(
9972 lc.as_str(),
9973 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
9974 ) {
9975 return Ok(Expr::FunctionCall {
9976 name: lc,
9977 args: Vec::new(),
9978 });
9979 }
9980 Ok(Expr::Column(ColumnName {
9981 qualifier: None,
9982 name: first,
9983 }))
9984 }
9985}
9986
9987fn extract_first_column(expr: &Expr) -> Option<String> {
9995 match expr {
9996 Expr::Column(cn) => Some(cn.name.clone()),
9997 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
9998 Expr::Binary { lhs, rhs, .. } => {
9999 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
10000 }
10001 Expr::Unary { expr: e, .. } => extract_first_column(e),
10002 _ => None,
10003 }
10004}
10005
10006fn maybe_not(expr: Expr, negated: bool) -> Expr {
10007 if negated {
10008 Expr::Unary {
10009 op: UnOp::Not,
10010 expr: Box::new(expr),
10011 }
10012 } else {
10013 expr
10014 }
10015}
10016
10017fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
10018 let pair = match tok {
10019 Token::Or => (BinOp::Or, 1),
10020 Token::And => (BinOp::And, 2),
10021 Token::Eq => (BinOp::Eq, 4),
10022 Token::NotEq => (BinOp::NotEq, 4),
10023 Token::Lt => (BinOp::Lt, 4),
10024 Token::LtEq => (BinOp::LtEq, 4),
10025 Token::Gt => (BinOp::Gt, 4),
10026 Token::GtEq => (BinOp::GtEq, 4),
10027 Token::L2Distance => (BinOp::L2Distance, 5),
10030 Token::InnerProduct => (BinOp::InnerProduct, 5),
10031 Token::CosineDistance => (BinOp::CosineDistance, 5),
10032 Token::Plus => (BinOp::Add, 6),
10033 Token::Minus => (BinOp::Sub, 6),
10034 Token::Concat => (BinOp::Concat, 6),
10037 Token::Pipe => (BinOp::BitOr, 6),
10050 Token::Amp => (BinOp::BitAnd, 6),
10051 Token::Star => (BinOp::Mul, 7),
10052 Token::Slash => (BinOp::Div, 7),
10053 Token::Percent => (BinOp::Mod, 7),
10054 Token::JsonGet => (BinOp::JsonGet, 7),
10058 Token::JsonGetText => (BinOp::JsonGetText, 7),
10059 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
10060 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
10061 Token::JsonContains => (BinOp::JsonContains, 7),
10062 Token::JsonContainedBy => (BinOp::JsonContainedBy, 7),
10063 Token::JsonKeyExists => (BinOp::JsonKeyExists, 7),
10064 Token::JsonKeysAny => (BinOp::JsonKeysAny, 7),
10065 Token::JsonKeysAll => (BinOp::JsonKeysAll, 7),
10066 Token::TsMatch => (BinOp::TsMatch, 4),
10070 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
10074 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
10075 Token::InetContains => (BinOp::InetContains, 4),
10076 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
10077 Token::InetOverlap => (BinOp::InetOverlap, 4),
10078 _ => return None,
10079 };
10080 Some(pair)
10081}
10082
10083#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
10084fn is_alias_stopword(s: &str) -> bool {
10096 matches!(
10097 s.to_ascii_lowercase().as_str(),
10098 "with"
10099 | "on"
10100 | "where"
10101 | "having"
10102 | "group"
10103 | "order"
10104 | "limit"
10105 | "offset"
10106 | "union"
10107 | "except"
10108 | "intersect"
10109 | "returning"
10110 | "set"
10111 | "values"
10112 | "for"
10113 | "lateral"
10114 | "left"
10115 | "right"
10116 | "inner"
10117 | "outer"
10118 | "full"
10119 | "cross"
10120 | "join"
10121 | "natural"
10122 | "using"
10123 | "fetch"
10124 )
10125}
10126
10127fn extract_numeric_literal(e: &Expr) -> Option<f32> {
10128 match e {
10129 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
10130 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
10131 Expr::Unary {
10132 op: UnOp::Neg,
10133 expr,
10134 } => extract_numeric_literal(expr).map(|x| -x),
10135 _ => None,
10136 }
10137}
10138
10139pub fn parse_interval_text(s: &str) -> Option<(i32, i32, i64)> {
10151 let parts: Vec<&str> = s.split_whitespace().collect();
10152 if parts.is_empty() || !parts.len().is_multiple_of(2) {
10153 return None;
10154 }
10155 let mut months: i32 = 0;
10156 let mut days: i32 = 0;
10157 let mut micros: i64 = 0;
10158 let mut i = 0;
10159 while i < parts.len() {
10160 let n: i64 = parts[i].parse().ok()?;
10161 let unit = parts[i + 1].to_ascii_lowercase();
10162 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
10163 match unit_stripped {
10164 "microsecond" => micros = micros.checked_add(n)?,
10165 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
10166 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
10167 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
10168 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
10169 "day" => {
10170 let n32 = i32::try_from(n).ok()?;
10171 days = days.checked_add(n32)?;
10172 }
10173 "week" => {
10174 let n32 = i32::try_from(n).ok()?;
10175 days = days.checked_add(n32.checked_mul(7)?)?;
10176 }
10177 "month" | "mon" => {
10181 let n32 = i32::try_from(n).ok()?;
10182 months = months.checked_add(n32)?;
10183 }
10184 "year" => {
10185 let n32 = i32::try_from(n).ok()?;
10186 months = months.checked_add(n32.checked_mul(12)?)?;
10187 }
10188 _ => return None,
10189 }
10190 i += 2;
10191 }
10192 Some((months, days, micros))
10193}
10194
10195fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
10206 Some(match ident.to_ascii_lowercase().as_str() {
10207 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
10208 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
10209 "bigint" => ColumnTypeName::BigInt,
10210 "float" | "double" | "real" => ColumnTypeName::Float,
10211 "text" => ColumnTypeName::Text,
10212 "bool" | "boolean" => ColumnTypeName::Bool,
10213 "date" => ColumnTypeName::Date,
10214 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
10215 "timestamptz" => ColumnTypeName::Timestamptz,
10216 "json" => ColumnTypeName::Json,
10217 "jsonb" => ColumnTypeName::Jsonb,
10218 "bytea" | "bytes" => ColumnTypeName::Bytes,
10219 "tsvector" => ColumnTypeName::TsVector,
10220 "tsquery" => ColumnTypeName::TsQuery,
10221 "uuid" => ColumnTypeName::Uuid,
10222 "interval" => ColumnTypeName::Interval,
10223 "time" => ColumnTypeName::Time,
10224 "year" => ColumnTypeName::Year,
10225 "timetz" => ColumnTypeName::TimeTz,
10226 "money" => ColumnTypeName::Money,
10227 _ => return None,
10228 })
10229}
10230
10231pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10258 parse_plpgsql_body(body)
10259}
10260
10261fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10262 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
10266 message: alloc::format!("plpgsql body lex error: {e}"),
10267 token_pos: 0,
10268 })?;
10269 let mut parser = Parser::new(tokens);
10270 parser.parse_plpgsql_block()
10271}
10272
10273#[cfg(test)]
10274mod tests {
10275 use super::*;
10276 use alloc::string::ToString;
10277
10278 fn parse(s: &str) -> Statement {
10279 parse_statement(s).expect("parse ok")
10280 }
10281
10282 #[test]
10292 fn release_usable_as_column_name_in_create_table() {
10293 let stmt =
10294 parse("CREATE TABLE events (id INT PRIMARY KEY, release TEXT NOT NULL, payload TEXT)");
10295 if let Statement::CreateTable(t) = stmt {
10296 let names: alloc::vec::Vec<&str> = t.columns.iter().map(|c| c.name.as_str()).collect();
10297 assert_eq!(names, alloc::vec!["id", "release", "payload"]);
10298 } else {
10299 panic!("expected CreateTable");
10300 }
10301 }
10302
10303 #[test]
10304 fn release_usable_as_column_ref_in_select_projection() {
10305 parse("SELECT id, release, payload FROM events WHERE id = 1");
10310 }
10311
10312 #[test]
10313 fn release_usable_as_column_ref_in_insert_column_list() {
10314 parse("INSERT INTO events (id, release, payload) VALUES (1, '1.0.0', 'data')");
10316 }
10317
10318 #[test]
10319 fn alter_column_drop_not_null_uses_keyword_drop_token() {
10320 parse("ALTER TABLE audit_logs ALTER COLUMN org_id DROP NOT NULL");
10325 }
10326
10327 #[test]
10328 fn create_index_accepts_parenthesised_expression_key() {
10329 parse(
10335 "CREATE INDEX IF NOT EXISTS events_bundle_id_idx \
10336 ON events ((payload->'bundle'->>'id'))",
10337 );
10338 }
10339
10340 #[test]
10344 fn nesting_budget_errors_cleanly() {
10345 let depth = MAX_NEST_DEPTH + 50;
10346 let sql = format!("SELECT {}1{}", "(".repeat(depth), ")".repeat(depth));
10347 let err = parse_statement(&sql).expect_err("must reject");
10348 assert!(err.message.contains("nests deeper"), "{err:?}");
10349 let sql = format!("SELECT {}1{}", "(".repeat(48), ")".repeat(48));
10351 parse(&sql);
10352 }
10353
10354 #[test]
10355 fn binary_chain_budget_errors_cleanly() {
10356 let sql = format!("SELECT 1{}", " + 1".repeat(MAX_BINARY_CHAIN + 50));
10357 let err = parse_statement(&sql).expect_err("must reject");
10358 assert!(err.message.contains("chained binary"), "{err:?}");
10359 let sql = format!("SELECT 1{}", " + 1".repeat(200));
10362 parse(&sql);
10363 }
10364
10365 #[test]
10366 fn in_list_unaffected_by_chain_budget() {
10367 let items: alloc::vec::Vec<String> = (0..20_000).map(|k| k.to_string()).collect();
10369 let sql = format!("SELECT 1 WHERE 5 IN ({})", items.join(","));
10370 let Statement::Select(s) = parse(&sql) else {
10371 panic!("expected select")
10372 };
10373 let Some(Expr::InList { list, negated, .. }) = s.where_ else {
10374 panic!("expected flat InList, got {:?}", s.where_)
10375 };
10376 assert_eq!(list.len(), 20_000);
10377 assert!(!negated);
10378 }
10379
10380 fn lit_int(n: i64) -> Expr {
10381 Expr::Literal(Literal::Integer(n))
10382 }
10383
10384 fn col(name: &str) -> Expr {
10385 Expr::Column(ColumnName {
10386 qualifier: None,
10387 name: name.into(),
10388 })
10389 }
10390
10391 #[test]
10392 fn select_single_integer() {
10393 let s = parse("SELECT 1");
10394 let Statement::Select(s) = s else {
10395 panic!("expected SELECT")
10396 };
10397 assert_eq!(s.items.len(), 1);
10398 assert!(s.from.is_none());
10399 assert!(s.where_.is_none());
10400 }
10401
10402 #[test]
10403 fn select_multiple_literal_kinds() {
10404 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
10405 let Statement::Select(s) = s else {
10406 panic!("expected SELECT")
10407 };
10408 assert_eq!(s.items.len(), 5);
10409 }
10410
10411 #[test]
10412 fn select_wildcard_from_table() {
10413 let s = parse("SELECT * FROM users");
10414 let Statement::Select(s) = s else {
10415 panic!("expected SELECT")
10416 };
10417 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
10418 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
10419 }
10420
10421 #[test]
10422 fn select_with_table_alias() {
10423 let s = parse("SELECT * FROM users AS u");
10424 let Statement::Select(s) = s else {
10425 panic!("expected SELECT")
10426 };
10427 let t = &s.from.as_ref().unwrap().primary;
10428 assert_eq!(t.name, "users");
10429 assert_eq!(t.alias.as_deref(), Some("u"));
10430 }
10431
10432 #[test]
10433 fn select_with_where_eq() {
10434 let s = parse("SELECT a FROM t WHERE a = 1");
10435 let Statement::Select(s) = s else {
10436 panic!("expected SELECT")
10437 };
10438 let w = s.where_.unwrap();
10439 assert_eq!(
10440 w,
10441 Expr::Binary {
10442 lhs: Box::new(col("a")),
10443 op: BinOp::Eq,
10444 rhs: Box::new(lit_int(1)),
10445 }
10446 );
10447 }
10448
10449 #[test]
10450 fn arithmetic_precedence() {
10451 let s = parse("SELECT 1 + 2 * 3");
10452 let Statement::Select(s) = s else {
10453 panic!("expected SELECT")
10454 };
10455 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10456 panic!("wildcard?")
10457 };
10458 assert_eq!(
10459 expr,
10460 &Expr::Binary {
10461 lhs: Box::new(lit_int(1)),
10462 op: BinOp::Add,
10463 rhs: Box::new(Expr::Binary {
10464 lhs: Box::new(lit_int(2)),
10465 op: BinOp::Mul,
10466 rhs: Box::new(lit_int(3)),
10467 }),
10468 }
10469 );
10470 }
10471
10472 #[test]
10473 fn parentheses_override_precedence() {
10474 let s = parse("SELECT (1 + 2) * 3");
10475 let Statement::Select(s) = s else {
10476 panic!("expected SELECT")
10477 };
10478 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10479 panic!()
10480 };
10481 assert_eq!(
10482 expr,
10483 &Expr::Binary {
10484 lhs: Box::new(Expr::Binary {
10485 lhs: Box::new(lit_int(1)),
10486 op: BinOp::Add,
10487 rhs: Box::new(lit_int(2)),
10488 }),
10489 op: BinOp::Mul,
10490 rhs: Box::new(lit_int(3)),
10491 }
10492 );
10493 }
10494
10495 #[test]
10496 fn not_binds_below_comparison() {
10497 let s = parse("SELECT NOT a = 1 FROM t");
10499 let Statement::Select(s) = s else {
10500 panic!("expected SELECT")
10501 };
10502 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10503 panic!()
10504 };
10505 assert_eq!(
10506 expr,
10507 &Expr::Unary {
10508 op: UnOp::Not,
10509 expr: Box::new(Expr::Binary {
10510 lhs: Box::new(col("a")),
10511 op: BinOp::Eq,
10512 rhs: Box::new(lit_int(1)),
10513 }),
10514 }
10515 );
10516 }
10517
10518 #[test]
10519 fn unary_minus_binds_above_multiplication() {
10520 let s = parse("SELECT -a * 2 FROM t");
10522 let Statement::Select(s) = s else {
10523 panic!("expected SELECT")
10524 };
10525 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10526 panic!()
10527 };
10528 assert_eq!(
10529 expr,
10530 &Expr::Binary {
10531 lhs: Box::new(Expr::Unary {
10532 op: UnOp::Neg,
10533 expr: Box::new(col("a")),
10534 }),
10535 op: BinOp::Mul,
10536 rhs: Box::new(lit_int(2)),
10537 }
10538 );
10539 }
10540
10541 #[test]
10542 fn qualified_column() {
10543 let s = parse("SELECT t.col FROM t");
10544 let Statement::Select(s) = s else {
10545 panic!("expected SELECT")
10546 };
10547 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10548 panic!()
10549 };
10550 assert_eq!(
10551 expr,
10552 &Expr::Column(ColumnName {
10553 qualifier: Some("t".into()),
10554 name: "col".into()
10555 })
10556 );
10557 }
10558
10559 #[test]
10560 fn select_item_alias_with_as() {
10561 let s = parse("SELECT a AS y FROM t");
10562 let Statement::Select(s) = s else {
10563 panic!("expected SELECT")
10564 };
10565 let SelectItem::Expr { alias, .. } = &s.items[0] else {
10566 panic!()
10567 };
10568 assert_eq!(alias.as_deref(), Some("y"));
10569 }
10570
10571 #[test]
10572 fn trailing_semicolon_accepted() {
10573 let s = parse("SELECT 1;");
10574 let Statement::Select(s) = s else {
10575 panic!("expected SELECT")
10576 };
10577 assert_eq!(s.items.len(), 1);
10578 }
10579
10580 #[test]
10581 fn boolean_chain_with_and_or_not() {
10582 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
10584 let Statement::Select(s) = s else {
10585 panic!("expected SELECT")
10586 };
10587 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10588 panic!()
10589 };
10590 let expected = Expr::Binary {
10591 lhs: Box::new(Expr::Unary {
10592 op: UnOp::Not,
10593 expr: Box::new(col("a")),
10594 }),
10595 op: BinOp::Or,
10596 rhs: Box::new(Expr::Binary {
10597 lhs: Box::new(col("b")),
10598 op: BinOp::And,
10599 rhs: Box::new(Expr::Unary {
10600 op: UnOp::Not,
10601 expr: Box::new(col("c")),
10602 }),
10603 }),
10604 };
10605 assert_eq!(expr, &expected);
10606 }
10607
10608 #[test]
10609 fn empty_input_errors() {
10610 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
10617 assert!(matches!(
10618 parse_statement(" \n\t ").unwrap(),
10619 Statement::Empty
10620 ));
10621 assert!(parse_statement("SELECT FROM WHERE").is_err());
10623 }
10624
10625 #[test]
10626 fn unmatched_paren_errors() {
10627 assert!(parse_statement("SELECT (1 + 2").is_err());
10628 }
10629
10630 #[test]
10631 fn display_round_trip_simple_select() {
10632 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
10633 let text = original.to_string();
10634 let again = parse_statement(&text).expect("re-parse");
10635 assert_eq!(original, again);
10636 }
10637
10638 #[test]
10641 fn create_table_single_column() {
10642 let s = parse("CREATE TABLE foo (a INT)");
10643 let Statement::CreateTable(c) = s else {
10644 panic!("expected CreateTable")
10645 };
10646 assert_eq!(c.name, "foo");
10647 assert_eq!(c.columns.len(), 1);
10648 assert_eq!(c.columns[0].name, "a");
10649 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10650 assert!(c.columns[0].nullable);
10651 }
10652
10653 #[test]
10654 fn create_table_multi_column_with_not_null_mix() {
10655 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
10656 let Statement::CreateTable(c) = s else {
10657 panic!()
10658 };
10659 assert_eq!(c.columns.len(), 4);
10660 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10661 assert!(!c.columns[0].nullable);
10662 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
10663 assert!(c.columns[1].nullable);
10664 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
10665 assert!(!c.columns[2].nullable);
10666 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
10667 }
10668
10669 #[test]
10670 fn create_table_bigint_supported() {
10671 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
10672 let Statement::CreateTable(c) = s else {
10673 panic!()
10674 };
10675 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
10676 }
10677
10678 #[test]
10679 fn create_table_vector_default_is_f32() {
10680 let s = parse("CREATE TABLE t (v VECTOR(128))");
10681 let Statement::CreateTable(c) = s else {
10682 panic!()
10683 };
10684 assert_eq!(
10685 c.columns[0].ty,
10686 ColumnTypeName::Vector {
10687 dim: 128,
10688 encoding: VecEncoding::F32,
10689 },
10690 );
10691 }
10692
10693 #[test]
10694 fn create_table_vector_using_sq8() {
10695 for sql in [
10698 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
10699 "CREATE TABLE t (v VECTOR(128) using sq8)",
10700 ] {
10701 let s = parse(sql);
10702 let Statement::CreateTable(c) = s else {
10703 panic!()
10704 };
10705 assert_eq!(
10706 c.columns[0].ty,
10707 ColumnTypeName::Vector {
10708 dim: 128,
10709 encoding: VecEncoding::Sq8,
10710 },
10711 "{sql}",
10712 );
10713 }
10714 }
10715
10716 #[test]
10717 fn create_table_vector_using_unknown_errors() {
10718 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
10727 assert!(
10728 err.message.contains("USING")
10729 || err.message.contains("using")
10730 || err.message.contains("')'")
10731 || err.message.contains("','"),
10732 "expected USING/column-list rejection, got: {}",
10733 err.message
10734 );
10735 }
10736
10737 #[test]
10738 fn vector_using_sq8_display_roundtrips() {
10739 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
10742 let Statement::CreateTable(c) = s else {
10743 panic!()
10744 };
10745 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
10746 }
10747
10748 #[test]
10749 fn parser_recognises_placeholders() {
10750 use crate::ast::{Expr, SelectItem, Statement};
10751 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
10753 let Statement::Select(sel) = s else { panic!() };
10754 assert!(matches!(
10755 sel.items[0],
10756 SelectItem::Expr {
10757 expr: Expr::Placeholder(1),
10758 alias: None
10759 }
10760 ));
10761 let SelectItem::Expr {
10763 expr: Expr::Binary { lhs, rhs, .. },
10764 ..
10765 } = &sel.items[1]
10766 else {
10767 panic!()
10768 };
10769 assert!(matches!(**lhs, Expr::Placeholder(2)));
10770 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
10771 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
10773 panic!()
10774 };
10775 assert!(matches!(**rhs, Expr::Placeholder(3)));
10776 }
10777
10778 #[test]
10779 fn parser_rejects_dollar_zero() {
10780 assert!(parse_statement("SELECT $0").is_err());
10782 }
10783
10784 #[test]
10785 fn placeholder_display_roundtrips() {
10786 let s = parse("SELECT $42 FROM t");
10789 let printed = s.to_string();
10790 assert!(printed.contains("$42"));
10791 let again = parse(&printed);
10792 assert_eq!(s, again);
10793 }
10794
10795 #[test]
10796 fn alter_index_rebuild_bare() {
10797 use crate::ast::{AlterIndexTarget, Statement};
10798 let s = parse("ALTER INDEX my_idx REBUILD");
10799 let Statement::AlterIndex(a) = s else {
10800 panic!("expected AlterIndex, got {s:?}")
10801 };
10802 assert_eq!(a.name, "my_idx");
10803 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
10804 }
10805
10806 #[test]
10807 fn alter_index_rebuild_with_encoding() {
10808 use crate::ast::{AlterIndexTarget, Statement};
10809 for (sql, want) in [
10810 (
10811 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
10812 VecEncoding::F32,
10813 ),
10814 (
10815 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
10816 VecEncoding::Sq8,
10817 ),
10818 (
10819 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10820 VecEncoding::F16,
10821 ),
10822 ] {
10823 let s = parse(sql);
10824 let Statement::AlterIndex(a) = s else {
10825 panic!("{sql}: expected AlterIndex")
10826 };
10827 assert_eq!(a.name, "my_idx");
10828 assert_eq!(
10829 a.target,
10830 AlterIndexTarget::Rebuild {
10831 encoding: Some(want)
10832 },
10833 "{sql}"
10834 );
10835 }
10836 }
10837
10838 #[test]
10839 fn alter_index_rebuild_unknown_encoding_errors() {
10840 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
10841 assert!(
10842 err.message.contains("unknown vector encoding"),
10843 "got: {}",
10844 err.message
10845 );
10846 }
10847
10848 #[test]
10849 fn alter_index_rebuild_display_roundtrips() {
10850 for (input, want) in [
10851 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
10852 (
10853 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
10854 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
10855 ),
10856 (
10857 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10858 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10859 ),
10860 ] {
10861 let s = parse(input);
10862 assert_eq!(s.to_string(), want);
10863 }
10864 }
10865
10866 #[test]
10867 fn create_table_unknown_type_defers_to_engine() {
10868 let stmt = parse_statement("CREATE TABLE x (a my_user_type)").unwrap();
10877 let Statement::CreateTable(t) = stmt else {
10878 panic!("expected CreateTable");
10879 };
10880 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("my_user_type"));
10881 }
10882
10883 #[test]
10884 fn create_table_missing_table_keyword_errors() {
10885 assert!(parse_statement("CREATE x (a INT)").is_err());
10886 }
10887
10888 #[test]
10892 fn parse_create_table_partition_by_range() {
10893 use crate::ast::{PartitionBySpec, PartitionKindAst};
10894 let stmt = parse_statement(
10895 "CREATE TABLE events_partitioned (id BIGINT NOT NULL, ts TIMESTAMPTZ NOT NULL, \
10896 payload JSONB) PARTITION BY RANGE (ts)",
10897 )
10898 .unwrap();
10899 let Statement::CreateTable(t) = stmt else {
10900 panic!("expected CreateTable");
10901 };
10902 assert!(t.partition_of.is_none(), "parent has no partition_of");
10903 assert_eq!(t.columns.len(), 3);
10904 let by = t.partition_by.as_ref().expect("expected PARTITION BY");
10905 assert_eq!(
10906 by,
10907 &PartitionBySpec {
10908 kind: PartitionKindAst::Range,
10909 key_columns: alloc::vec!["ts".to_string()],
10910 }
10911 );
10912 assert!(
10916 t.to_string().contains("PARTITION BY RANGE (ts)"),
10917 "Display lost PARTITION BY suffix: {t}"
10918 );
10919 }
10920
10921 #[test]
10922 fn parse_create_table_partition_of_range() {
10923 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
10924 let stmt = parse_statement(
10925 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned \
10926 FOR VALUES FROM ('2026-06-01 00:00:00+00') TO ('2026-07-01 00:00:00+00')",
10927 )
10928 .unwrap();
10929 let Statement::CreateTable(t) = stmt else {
10930 panic!("expected CreateTable");
10931 };
10932 assert!(t.columns.is_empty(), "child inherits columns from parent");
10933 assert!(t.partition_by.is_none());
10934 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
10935 assert_eq!(of.parent_name, "events_partitioned");
10936 let PartitionOfSpec { bounds, .. } = of.clone();
10937 match bounds {
10938 PartitionOfBoundsAst::Range { lower, upper } => {
10939 assert!(lower.to_string().contains("2026-06-01"));
10940 assert!(upper.to_string().contains("2026-07-01"));
10941 }
10942 PartitionOfBoundsAst::Default => panic!("expected Range, got Default"),
10943 }
10944 let s = t.to_string();
10948 assert!(
10949 s.contains("PARTITION OF events_partitioned"),
10950 "Display lost PARTITION OF: {s}"
10951 );
10952 assert!(s.contains("FOR VALUES FROM"), "Display lost FROM: {s}");
10953 assert!(s.contains(") TO ("), "Display lost TO: {s}");
10954 }
10955
10956 #[test]
10957 fn parse_create_table_partition_of_default() {
10958 use crate::ast::PartitionOfBoundsAst;
10959 let stmt =
10960 parse_statement("CREATE TABLE events_default PARTITION OF events_partitioned DEFAULT")
10961 .unwrap();
10962 let Statement::CreateTable(t) = stmt else {
10963 panic!("expected CreateTable");
10964 };
10965 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
10966 assert_eq!(of.parent_name, "events_partitioned");
10967 assert!(matches!(of.bounds, PartitionOfBoundsAst::Default));
10968 assert!(
10969 t.to_string()
10970 .contains("PARTITION OF events_partitioned DEFAULT"),
10971 "Display lost DEFAULT: {t}"
10972 );
10973 }
10974
10975 #[test]
10976 fn parse_create_table_partition_of_rejects_columns() {
10977 let err = parse_statement(
10981 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned (id BIGINT) \
10982 FOR VALUES FROM ('a') TO ('b')",
10983 );
10984 assert!(err.is_err(), "expected parse error for explicit columns");
10985 let msg = format!("{}", err.unwrap_err());
10986 assert!(
10987 msg.contains("PARTITION OF") && msg.contains("column"),
10988 "error should mention PARTITION OF + columns: {msg}"
10989 );
10990 }
10991
10992 #[test]
10993 fn insert_single_value() {
10994 let s = parse("INSERT INTO foo VALUES (42)");
10995 let Statement::Insert(i) = s else {
10996 panic!("expected Insert")
10997 };
10998 assert_eq!(i.table, "foo");
10999 assert_eq!(i.rows.len(), 1);
11000 assert_eq!(i.rows[0].len(), 1);
11001 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
11002 }
11003
11004 #[test]
11005 fn insert_multi_value_with_mixed_literals() {
11006 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
11007 let Statement::Insert(i) = s else { panic!() };
11008 assert_eq!(i.rows.len(), 1);
11009 assert_eq!(i.rows[0].len(), 5);
11010 }
11011
11012 #[test]
11013 fn insert_missing_into_errors() {
11014 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
11015 }
11016
11017 #[test]
11018 fn create_table_round_trip() {
11019 let original =
11020 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
11021 let text = original.to_string();
11022 let again = parse_statement(&text).expect("re-parse");
11023 assert_eq!(original, again);
11024 }
11025
11026 #[test]
11027 fn insert_round_trip_with_negation_and_string() {
11028 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
11029 let text = original.to_string();
11030 let again = parse_statement(&text).expect("re-parse");
11031 assert_eq!(original, again);
11032 }
11033
11034 #[test]
11035 fn unknown_keyword_at_statement_start_errors() {
11036 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
11039 assert!(err.message.contains("expected SELECT"));
11040 }
11041
11042 #[test]
11045 fn create_index_basic() {
11046 let s = parse("CREATE INDEX idx_id ON users (id)");
11047 let Statement::CreateIndex(c) = s else {
11048 panic!("expected CreateIndex")
11049 };
11050 assert_eq!(c.name, "idx_id");
11051 assert_eq!(c.table, "users");
11052 assert_eq!(c.column, "id");
11053 }
11054
11055 #[test]
11056 fn create_index_missing_on_errors() {
11057 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
11058 }
11059
11060 #[test]
11061 fn create_index_missing_paren_errors() {
11062 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
11063 }
11064
11065 #[test]
11066 fn create_index_round_trip() {
11067 let original = parse("CREATE INDEX by_name ON users (name)");
11068 let again = parse_statement(&original.to_string()).unwrap();
11069 assert_eq!(original, again);
11070 }
11071
11072 #[test]
11075 fn create_unique_index_basic() {
11076 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
11077 let Statement::CreateIndex(c) = s else {
11078 panic!("expected CreateIndex");
11079 };
11080 assert!(c.is_unique);
11081 assert_eq!(c.column, "a");
11082 assert!(c.partial_predicate.is_none());
11083 }
11084
11085 #[test]
11086 fn create_unique_index_partial() {
11087 let s = parse(
11089 "CREATE UNIQUE INDEX idx_email_templates_user_default \
11090 ON email_templates (user_address) WHERE is_default = true",
11091 );
11092 let Statement::CreateIndex(c) = s else {
11093 panic!("expected CreateIndex");
11094 };
11095 assert!(c.is_unique);
11096 assert_eq!(c.table, "email_templates");
11097 assert_eq!(c.column, "user_address");
11098 assert!(c.partial_predicate.is_some());
11099 }
11100
11101 #[test]
11102 fn create_unique_index_composite_with_predicate() {
11103 let s = parse(
11105 "CREATE UNIQUE INDEX uq_calendar_events_instance \
11106 ON calendar_events (calendar_id, uid, recurrence_id) \
11107 WHERE recurrence_id IS NOT NULL",
11108 );
11109 let Statement::CreateIndex(c) = s else {
11110 panic!("expected CreateIndex");
11111 };
11112 assert!(c.is_unique);
11113 assert_eq!(c.column, "calendar_id");
11114 assert_eq!(
11115 c.extra_columns,
11116 vec!["uid".to_string(), "recurrence_id".to_string()]
11117 );
11118 assert!(c.partial_predicate.is_some());
11119 }
11120
11121 #[test]
11122 fn create_unique_index_using_btree_ok() {
11123 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
11124 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
11125 }
11126
11127 #[test]
11128 fn create_unique_index_using_hnsw_rejected() {
11129 let err =
11130 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
11131 assert!(err.message.contains("UNIQUE"), "{}", err.message);
11132 }
11133
11134 #[test]
11135 fn create_unique_index_round_trip() {
11136 let original = parse(
11137 "CREATE UNIQUE INDEX uq_calendar_events_master \
11138 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
11139 );
11140 let again = parse_statement(&original.to_string()).unwrap();
11141 assert_eq!(original, again);
11142 }
11143
11144 #[test]
11145 fn create_unique_without_index_errors() {
11146 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
11147 assert!(err.message.contains("INDEX"), "{}", err.message);
11148 }
11149
11150 #[test]
11153 fn create_table_bytea_column() {
11154 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
11155 let Statement::CreateTable(c) = s else {
11156 panic!("expected CreateTable");
11157 };
11158 assert_eq!(c.columns.len(), 2);
11159 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
11160 assert!(!c.columns[1].nullable);
11161 }
11162
11163 #[test]
11164 fn create_table_bytes_alias_column() {
11165 let s = parse("CREATE TABLE t (blob BYTES)");
11166 let Statement::CreateTable(c) = s else {
11167 panic!("expected CreateTable");
11168 };
11169 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
11170 }
11171
11172 #[test]
11173 fn bytea_round_trip_display() {
11174 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
11175 let again = parse_statement(&original.to_string()).unwrap();
11176 assert_eq!(original, again);
11177 }
11178
11179 #[test]
11182 fn begin_commit_rollback_parse_as_unit_variants() {
11183 assert_eq!(parse("BEGIN"), Statement::Begin);
11184 assert_eq!(parse("COMMIT"), Statement::Commit);
11185 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
11186 assert_eq!(parse("BEGIN;"), Statement::Begin);
11188 }
11189
11190 #[test]
11193 fn inner_product_binop_parses() {
11194 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
11195 let Statement::Select(s) = s else { panic!() };
11196 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11197 panic!()
11198 };
11199 assert!(matches!(
11200 expr,
11201 Expr::Binary {
11202 op: BinOp::InnerProduct,
11203 ..
11204 }
11205 ));
11206 }
11207
11208 #[test]
11209 fn cosine_distance_binop_parses() {
11210 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
11211 let Statement::Select(s) = s else { panic!() };
11212 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11213 panic!()
11214 };
11215 assert!(matches!(
11216 expr,
11217 Expr::Binary {
11218 op: BinOp::CosineDistance,
11219 ..
11220 }
11221 ));
11222 }
11223
11224 #[test]
11225 fn vector_cast_postfix_wraps_string_literal() {
11226 let s = parse("SELECT '[1,2,3]'::vector FROM t");
11227 let Statement::Select(s) = s else { panic!() };
11228 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11229 panic!()
11230 };
11231 assert!(matches!(
11232 expr,
11233 Expr::Cast {
11234 target: CastTarget::Vector,
11235 ..
11236 }
11237 ));
11238 }
11239
11240 #[test]
11241 fn unsupported_cast_target_errors() {
11242 let err = parse_statement("SELECT 1::, FROM t").unwrap_err();
11249 assert!(err.message.contains("expected type ident after `::`"));
11250 }
11251
11252 #[test]
11253 fn tx_statements_round_trip() {
11254 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
11255 let original = parse(q);
11256 let again = parse_statement(&original.to_string()).unwrap();
11257 assert_eq!(original, again);
11258 }
11259 }
11260
11261 #[test]
11262 fn interval_text_parsing_units() {
11263 assert_eq!(parse_interval_text("1 day"), Some((0, 1, 0)));
11267 assert_eq!(
11268 parse_interval_text("24 hours"),
11269 Some((0, 0, 86_400_000_000))
11270 );
11271 assert_eq!(parse_interval_text("1 second"), Some((0, 0, 1_000_000)));
11272 assert_eq!(parse_interval_text("1 month"), Some((1, 0, 0)));
11273 assert_eq!(parse_interval_text("2 years"), Some((24, 0, 0)));
11274 assert_eq!(parse_interval_text("1 week"), Some((0, 7, 0)));
11275 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0, 0)));
11277 assert_eq!(
11278 parse_interval_text("1 day 2 hours"),
11279 Some((0, 1, 7_200_000_000))
11280 );
11281 assert_eq!(parse_interval_text("-1 day"), Some((0, -1, 0)));
11283 assert_eq!(parse_interval_text(""), None);
11285 assert_eq!(parse_interval_text("garbage"), None);
11286 assert_eq!(parse_interval_text("1 fortnight"), None);
11287 assert_eq!(parse_interval_text("1"), None);
11288 }
11289
11290 #[test]
11291 fn interval_literal_roundtrips_via_display() {
11292 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
11293 let s = parsed.to_string();
11294 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
11296 let again = parse_statement(&s).unwrap();
11298 assert_eq!(parsed, again);
11299 }
11300
11301 #[test]
11304 fn parser_recognises_create_publication_bare() {
11305 let s = parse("CREATE PUBLICATION pub_a");
11306 let Statement::CreatePublication(p) = s else {
11307 panic!("expected CreatePublication, got {s:?}")
11308 };
11309 assert_eq!(p.name, "pub_a");
11310 assert_eq!(p.scope, PublicationScope::AllTables);
11311 }
11312
11313 #[test]
11314 fn parser_recognises_create_publication_for_all_tables() {
11315 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
11316 let Statement::CreatePublication(p) = s else {
11317 panic!("expected CreatePublication, got {s:?}")
11318 };
11319 assert_eq!(p.name, "pub_a");
11320 assert_eq!(p.scope, PublicationScope::AllTables);
11321 }
11322
11323 #[test]
11324 fn parser_recognises_drop_publication() {
11325 let s = parse("DROP PUBLICATION pub_a");
11326 let Statement::DropPublication(name) = s else {
11327 panic!("expected DropPublication, got {s:?}")
11328 };
11329 assert_eq!(name, "pub_a");
11330 }
11331
11332 #[test]
11333 fn parser_recognises_for_table_list() {
11334 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
11335 let Statement::CreatePublication(p) = s else {
11336 panic!("expected CreatePublication, got {s:?}")
11337 };
11338 assert_eq!(p.name, "pub_a");
11339 let PublicationScope::ForTables(ts) = p.scope else {
11340 panic!("expected ForTables scope")
11341 };
11342 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
11343 }
11344
11345 #[test]
11346 fn parser_recognises_for_tables_plural() {
11347 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
11349 let Statement::CreatePublication(p) = s else {
11350 panic!("expected CreatePublication, got {s:?}")
11351 };
11352 let PublicationScope::ForTables(ts) = p.scope else {
11353 panic!("expected ForTables")
11354 };
11355 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11356 }
11357
11358 #[test]
11359 fn parser_recognises_for_all_tables_except_list() {
11360 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
11361 let Statement::CreatePublication(p) = s else {
11362 panic!()
11363 };
11364 let PublicationScope::AllTablesExcept(ts) = p.scope else {
11365 panic!("expected AllTablesExcept")
11366 };
11367 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11368 }
11369
11370 #[test]
11371 fn parser_rejects_for_table_with_empty_list() {
11372 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
11374 .expect_err("must error on empty list");
11375 assert!(!err.message.is_empty());
11378 }
11379
11380 #[test]
11381 fn parser_recognises_show_publications() {
11382 let s = parse("SHOW PUBLICATIONS");
11385 assert!(matches!(s, Statement::ShowPublications));
11386 }
11387
11388 #[test]
11391 fn parser_recognises_create_subscription_single_publication() {
11392 let s = parse(
11393 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
11394 );
11395 let Statement::CreateSubscription(c) = s else {
11396 panic!("expected CreateSubscription, got {s:?}")
11397 };
11398 assert_eq!(c.name, "sub_a");
11399 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
11400 assert_eq!(c.publications, alloc::vec!["pub_a"]);
11401 }
11402
11403 #[test]
11404 fn parser_recognises_create_subscription_multi_publication() {
11405 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
11406 let Statement::CreateSubscription(c) = s else {
11407 panic!()
11408 };
11409 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
11410 }
11411
11412 #[test]
11413 fn parser_rejects_create_subscription_missing_connection() {
11414 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
11415 .expect_err("must error on missing CONNECTION");
11416 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
11417 }
11418
11419 #[test]
11420 fn parser_rejects_create_subscription_missing_publication() {
11421 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
11422 .expect_err("must error on missing PUBLICATION");
11423 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
11424 }
11425
11426 #[test]
11427 fn parser_recognises_drop_subscription() {
11428 let s = parse("DROP SUBSCRIPTION sub_a");
11429 let Statement::DropSubscription(name) = s else {
11430 panic!("expected DropSubscription, got {s:?}")
11431 };
11432 assert_eq!(name, "sub_a");
11433 }
11434
11435 #[test]
11436 fn parser_recognises_show_subscriptions() {
11437 let s = parse("SHOW SUBSCRIPTIONS");
11438 assert!(matches!(s, Statement::ShowSubscriptions));
11439 }
11440
11441 #[test]
11442 fn parser_recognises_wait_for_wal_position_no_timeout() {
11443 let s = parse("WAIT FOR WAL POSITION 12345");
11444 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11445 panic!("expected WaitForWalPosition, got {s:?}")
11446 };
11447 assert_eq!(pos, 12345);
11448 assert!(timeout_ms.is_none());
11449 }
11450
11451 #[test]
11452 fn parser_recognises_wait_for_wal_position_with_timeout() {
11453 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
11454 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11455 panic!()
11456 };
11457 assert_eq!(pos, 67890);
11458 assert_eq!(timeout_ms, Some(5000));
11459 }
11460
11461 #[test]
11462 fn parser_rejects_wait_with_negative_position() {
11463 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
11469 assert!(!err.message.is_empty());
11470 }
11471
11472 #[test]
11473 fn parser_recognises_bare_analyze() {
11474 let s = parse("ANALYZE");
11475 assert!(matches!(s, Statement::Analyze(None)));
11476 }
11477
11478 #[test]
11479 fn parser_recognises_analyze_with_table() {
11480 let s = parse("ANALYZE users");
11481 let Statement::Analyze(Some(name)) = s else {
11482 panic!("expected Analyze, got {s:?}")
11483 };
11484 assert_eq!(name, "users");
11485 }
11486
11487 #[test]
11488 fn parser_recognises_analyze_with_quoted_table() {
11489 let s = parse("ANALYZE \"Mixed Case\"");
11490 let Statement::Analyze(Some(name)) = s else {
11491 panic!()
11492 };
11493 assert_eq!(name, "Mixed Case");
11494 }
11495
11496 #[test]
11497 fn parser_rejects_analyze_with_garbage_token() {
11498 let err = parse_statement("ANALYZE 42").expect_err("must error");
11499 assert!(!err.message.is_empty());
11500 }
11501
11502 #[test]
11503 fn analyze_display_roundtrips() {
11504 for sql in ["ANALYZE", "ANALYZE users"] {
11505 let s = parse(sql);
11506 let printed = s.to_string();
11507 let again = parse_statement(&printed)
11508 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11509 assert_eq!(s, again);
11510 }
11511 }
11512
11513 #[test]
11514 fn wait_for_display_roundtrips() {
11515 for sql in [
11516 "WAIT FOR WAL POSITION 12345",
11517 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
11518 ] {
11519 let s = parse(sql);
11520 let printed = s.to_string();
11521 let again = parse_statement(&printed)
11522 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11523 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11524 }
11525 }
11526
11527 #[test]
11528 fn subscription_ddl_display_roundtrips() {
11529 for sql in [
11530 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
11531 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
11532 "DROP SUBSCRIPTION sub_a",
11533 "SHOW SUBSCRIPTIONS",
11534 ] {
11535 let s = parse(sql);
11536 let printed = s.to_string();
11537 let again = parse_statement(&printed)
11538 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11539 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11540 }
11541 }
11542
11543 #[test]
11544 fn parser_drop_dispatches_user_vs_publication() {
11545 let s = parse("DROP USER 'alice'");
11548 let Statement::DropUser(name) = s else {
11549 panic!("expected DropUser, got {s:?}")
11550 };
11551 assert_eq!(name, "alice");
11552 let s = parse("DROP PUBLICATION p1");
11554 assert!(matches!(s, Statement::DropPublication(_)));
11555 }
11556
11557 #[test]
11558 fn publication_ddl_display_roundtrips() {
11559 for sql in [
11562 "CREATE PUBLICATION pub_a",
11563 "CREATE PUBLICATION pub_a FOR ALL TABLES",
11564 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
11565 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
11566 "DROP PUBLICATION pub_a",
11567 "SHOW PUBLICATIONS",
11568 ] {
11569 let s = parse(sql);
11570 let printed = s.to_string();
11571 let again = parse_statement(&printed)
11572 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11573 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11574 }
11575 }
11576
11577 #[test]
11580 fn create_function_returns_trigger_plpgsql_minimal() {
11581 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
11582 let s = parse(sql);
11583 let Statement::CreateFunction(f) = s else {
11584 panic!("expected CreateFunction");
11585 };
11586 assert_eq!(f.name, "noop");
11587 assert!(!f.or_replace);
11588 assert!(f.args.is_empty());
11589 assert!(matches!(f.returns, FunctionReturn::Trigger));
11590 assert_eq!(f.language, "plpgsql");
11591 let FunctionBody::PlPgSql(block) = f.body else {
11592 panic!("expected PlPgSql body");
11593 };
11594 assert_eq!(block.statements.len(), 1);
11595 assert!(matches!(
11596 block.statements[0],
11597 PlPgSqlStmt::Return(ReturnTarget::New)
11598 ));
11599 }
11600
11601 #[test]
11602 fn create_function_or_replace_with_assignment() {
11603 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
11606BEGIN
11607 NEW.search_vector := to_tsvector('english', NEW.subject);
11608 RETURN NEW;
11609END;
11610$$";
11611 let s = parse(sql);
11612 let Statement::CreateFunction(f) = s else {
11613 panic!("expected CreateFunction");
11614 };
11615 assert!(f.or_replace);
11616 let FunctionBody::PlPgSql(block) = &f.body else {
11617 panic!("expected PlPgSql body");
11618 };
11619 assert_eq!(block.statements.len(), 2);
11620 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
11622 panic!("expected Assign as first stmt");
11623 };
11624 match target {
11625 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
11626 other => panic!("expected NEW.col, got {other:?}"),
11627 }
11628 assert!(matches!(
11630 block.statements[1],
11631 PlPgSqlStmt::Return(ReturnTarget::New)
11632 ));
11633 }
11634
11635 #[test]
11636 fn create_trigger_after_insert_or_update() {
11637 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
11638 let s = parse(sql);
11639 let Statement::CreateTrigger(t) = s else {
11640 panic!("expected CreateTrigger");
11641 };
11642 assert_eq!(t.name, "tg");
11643 assert_eq!(t.table, "messages");
11644 assert_eq!(t.timing, TriggerTiming::After);
11645 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
11646 assert_eq!(t.for_each, TriggerForEach::Row);
11647 assert_eq!(t.function, "update_sv");
11648 }
11649
11650 #[test]
11651 fn create_trigger_before_delete_execute_procedure_alias() {
11652 let sql =
11654 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
11655 let s = parse(sql);
11656 let Statement::CreateTrigger(t) = s else {
11657 panic!("expected CreateTrigger");
11658 };
11659 assert_eq!(t.timing, TriggerTiming::Before);
11660 assert_eq!(t.events, vec![TriggerEvent::Delete]);
11661 }
11662
11663 #[test]
11664 fn drop_trigger_if_exists_round_trips() {
11665 let s = Statement::DropTrigger {
11670 name: "tg".into(),
11671 table: "messages".into(),
11672 if_exists: true,
11673 };
11674 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
11675 }
11676
11677 #[test]
11678 fn trigger_ddl_display_roundtrips_through_parser() {
11679 for sql in [
11683 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
11684 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
11685 ] {
11686 let s = parse(sql);
11687 let printed = s.to_string();
11688 let again = parse_statement(&printed)
11689 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11690 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11691 }
11692 }
11693}