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 if matches!(self.peek(), Token::LParen) {
516 self.advance();
517 let opt = match self.peek().clone() {
518 Token::Ident(s) | Token::QuotedIdent(s) => s,
519 other => {
520 return Err(self.err(format!(
521 "expected option keyword inside EXPLAIN (…), got {other:?}"
522 )));
523 }
524 };
525 if !opt.eq_ignore_ascii_case("suggest") {
526 return Err(self.err(format!(
527 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
528 )));
529 }
530 self.advance();
531 if !matches!(self.peek(), Token::RParen) {
532 return Err(self.err(format!(
533 "expected ')' after EXPLAIN option, got {:?}",
534 self.peek()
535 )));
536 }
537 self.advance();
538 suggest = true;
539 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
540 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
541 {
542 self.advance();
543 analyze = true;
544 }
545 let inner = self.parse_select_stmt()?;
546 let Statement::Select(s) = inner else {
547 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
548 };
549 Ok(Statement::Explain(crate::ast::ExplainStatement {
550 analyze,
551 inner: Box::new(s),
552 suggest,
553 }))
554 }
555 Token::Create => self.parse_create_stmt(),
556 Token::Insert => self.parse_insert_stmt(),
557 Token::Begin => {
558 self.advance();
559 Ok(Statement::Begin)
560 }
561 Token::Commit => {
562 self.advance();
563 Ok(Statement::Commit)
564 }
565 Token::Rollback => {
566 self.advance();
567 if matches!(self.peek(), Token::To) {
571 self.advance();
572 if matches!(self.peek(), Token::Savepoint) {
573 self.advance();
574 }
575 let name = self.expect_ident_like()?;
576 Ok(Statement::RollbackToSavepoint(name))
577 } else {
578 Ok(Statement::Rollback)
579 }
580 }
581 Token::Savepoint => {
582 self.advance();
583 let name = self.expect_ident_like()?;
584 Ok(Statement::Savepoint(name))
585 }
586 Token::Release => {
587 self.advance();
588 if matches!(self.peek(), Token::Savepoint) {
591 self.advance();
592 }
593 let name = self.expect_ident_like()?;
594 Ok(Statement::ReleaseSavepoint(name))
595 }
596 Token::Show => {
597 self.advance();
598 let target = match self.advance() {
604 Token::Tables => "tables".to_string(),
605 Token::Create => "create".to_string(),
609 Token::Index => "index".to_string(),
612 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
613 other => {
614 return Err(self.err(format!(
615 "expected SHOW target, got {other:?}"
616 )));
617 }
618 };
619 match target.as_str() {
620 "tables" => Ok(Statement::ShowTables),
621 "users" => Ok(Statement::ShowUsers),
622 "indexes" | "index" | "keys" => {
632 if !matches!(self.peek(), Token::From) {
633 return Err(self.err(format!(
634 "expected FROM after SHOW INDEXES, got {:?}",
635 self.peek()
636 )));
637 }
638 self.advance();
639 let table = self.expect_ident_like()?;
640 Ok(Statement::ShowIndexes(table))
641 }
642 "status" => Ok(Statement::ShowStatus),
647 "variables" => Ok(Statement::ShowVariables),
648 "processlist" => Ok(Statement::ShowProcesslist),
650 "create" => {
651 let kind = match self.advance() {
654 Token::Ident(s) | Token::QuotedIdent(s) => s,
655 Token::Table => "table".to_string(),
656 other => {
657 return Err(self.err(format!(
658 "expected TABLE after SHOW CREATE, got {other:?}"
659 )));
660 }
661 };
662 if !kind.eq_ignore_ascii_case("table") {
663 return Err(self.err(format!(
664 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
665 )));
666 }
667 let name = self.expect_ident_like()?;
668 Ok(Statement::ShowCreateTable(name))
669 }
670 "databases" | "schemas" => Ok(Statement::ShowDatabases),
676 "publications" => Ok(Statement::ShowPublications),
681 "subscriptions" => Ok(Statement::ShowSubscriptions),
683 "columns" => {
684 if !matches!(self.peek(), Token::From) {
685 return Err(self.err(format!(
686 "expected FROM after SHOW COLUMNS, got {:?}",
687 self.peek()
688 )));
689 }
690 self.advance();
691 let table = self.expect_ident_like()?;
692 Ok(Statement::ShowColumns(table))
693 }
694 other => Err(self.err(format!(
695 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
696 ))),
697 }
698 }
699 Token::Drop => {
705 self.advance();
706 match self.peek() {
707 Token::Publication => {
708 self.advance();
709 let name = self.expect_ident_or_string()?;
710 Ok(Statement::DropPublication(name))
711 }
712 Token::Subscription => {
713 self.advance();
714 let name = self.expect_ident_or_string()?;
715 Ok(Statement::DropSubscription(name))
716 }
717 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
718 self.advance();
719 let name = self.expect_ident_or_string()?;
720 Ok(Statement::DropUser(name))
721 }
722 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
724 self.advance();
725 let if_exists = self.consume_if_exists();
726 let name = self.expect_ident_like()?;
727 if !matches!(self.peek(), Token::On) {
729 return Err(self.err(alloc::format!(
730 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
731 self.peek()
732 )));
733 }
734 self.advance();
735 let table = self.expect_ident_like()?;
736 Ok(Statement::DropTrigger {
737 name,
738 table,
739 if_exists,
740 })
741 }
742 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
746 self.advance();
747 let if_exists = self.consume_if_exists();
748 let name = self.expect_ident_like()?;
749 if matches!(self.peek(), Token::LParen) {
751 self.advance();
752 let mut depth = 1usize;
754 while depth > 0 {
755 match self.peek() {
756 Token::LParen => depth += 1,
757 Token::RParen => depth -= 1,
758 Token::Eof => {
759 return Err(self.err(alloc::format!(
760 "unterminated arg list in DROP FUNCTION {name:?}"
761 )));
762 }
763 _ => {}
764 }
765 self.advance();
766 }
767 }
768 Ok(Statement::DropFunction { name, if_exists })
769 }
770 Token::Table => {
778 self.advance();
779 let if_exists = self.consume_if_exists();
780 let mut names: Vec<String> = Vec::new();
781 loop {
782 names.push(self.expect_ident_like()?);
783 if matches!(self.peek(), Token::Comma) {
784 self.advance();
785 continue;
786 }
787 break;
788 }
789 if matches!(
790 self.peek(),
791 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
792 || s.eq_ignore_ascii_case("restrict")
793 ) {
794 self.advance();
795 }
796 Ok(Statement::DropTable { names, if_exists })
797 }
798 Token::Index => {
804 self.advance();
805 let if_exists = self.consume_if_exists();
806 let name = self.expect_ident_like()?;
807 if matches!(
808 self.peek(),
809 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
810 || s.eq_ignore_ascii_case("restrict")
811 ) {
812 self.advance();
813 }
814 Ok(Statement::DropIndex { name, if_exists })
815 }
816 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
822 self.advance();
823 let if_exists = self.consume_if_exists();
824 let mut names = vec![self.expect_ident_like()?];
825 while matches!(self.peek(), Token::Comma) {
826 self.advance();
827 names.push(self.expect_ident_like()?);
828 }
829 if matches!(
830 self.peek(),
831 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
832 || s.eq_ignore_ascii_case("restrict")
833 ) {
834 self.advance();
835 }
836 Ok(Statement::DropSchema { names, if_exists })
837 }
838 Token::Ident(s) | Token::QuotedIdent(s)
841 if s.eq_ignore_ascii_case("type") =>
842 {
843 self.advance();
844 let if_exists = self.consume_if_exists();
845 let mut names = vec![self.expect_ident_like()?];
846 while matches!(self.peek(), Token::Comma) {
847 self.advance();
848 names.push(self.expect_ident_like()?);
849 }
850 if matches!(
851 self.peek(),
852 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
853 || s.eq_ignore_ascii_case("restrict")
854 ) {
855 self.advance();
856 }
857 Ok(Statement::DropType { names, if_exists })
858 }
859 Token::Ident(s) | Token::QuotedIdent(s)
862 if s.eq_ignore_ascii_case("domain") =>
863 {
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::DropDomain { names, if_exists })
879 }
880 Token::Ident(s) | Token::QuotedIdent(s)
883 if s.eq_ignore_ascii_case("materialized") =>
884 {
885 self.advance();
886 let nxt = self.peek().clone();
887 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
888 {
889 return Err(self.err(alloc::format!(
890 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
891 )));
892 }
893 self.advance();
894 let if_exists = self.consume_if_exists();
895 let mut names = vec![self.expect_ident_like()?];
896 while matches!(self.peek(), Token::Comma) {
897 self.advance();
898 names.push(self.expect_ident_like()?);
899 }
900 if matches!(
901 self.peek(),
902 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
903 || s.eq_ignore_ascii_case("restrict")
904 ) {
905 self.advance();
906 }
907 Ok(Statement::DropMaterializedView { names, if_exists })
908 }
909 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
912 self.advance();
913 let if_exists = self.consume_if_exists();
914 let mut names = vec![self.expect_ident_like()?];
915 while matches!(self.peek(), Token::Comma) {
916 self.advance();
917 names.push(self.expect_ident_like()?);
918 }
919 if matches!(
920 self.peek(),
921 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
922 || s.eq_ignore_ascii_case("restrict")
923 ) {
924 self.advance();
925 }
926 Ok(Statement::DropView { names, if_exists })
927 }
928 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
932 self.advance();
933 let if_exists = self.consume_if_exists();
934 let mut names = vec![self.expect_ident_like()?];
935 while matches!(self.peek(), Token::Comma) {
936 self.advance();
937 names.push(self.expect_ident_like()?);
938 }
939 if matches!(
940 self.peek(),
941 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
942 || s.eq_ignore_ascii_case("restrict")
943 ) {
944 self.advance();
945 }
946 Ok(Statement::DropSequence { names, if_exists })
947 }
948 other => Err(self.err(format!(
949 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
950 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
951 ))),
952 }
953 }
954 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
956 self.advance();
957 let nxt = self.peek().clone();
958 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
959 {
960 return Err(self.err(alloc::format!(
961 "expected MATERIALIZED after REFRESH, got {nxt:?}"
962 )));
963 }
964 self.advance();
965 let nxt2 = self.peek().clone();
966 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
967 {
968 return Err(self.err(alloc::format!(
969 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
970 )));
971 }
972 self.advance();
973 let name = self.expect_ident_like()?;
974 let with_data = self.parse_optional_with_data(true)?;
975 Ok(Statement::RefreshMaterializedView { name, with_data })
976 }
977 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
978 self.advance();
979 self.parse_update_after_keyword()
980 }
981 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
982 self.advance();
983 self.parse_delete_after_keyword()
984 }
985 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
989 self.advance();
990 self.parse_alter_after_keyword()
991 }
992 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
996 self.advance();
997 self.parse_wait_after_keyword()
998 }
999 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
1010 self.advance();
1011 let next = self.peek().clone();
1012 let cold = match next {
1013 Token::Ident(s) | Token::QuotedIdent(s) => s,
1014 _ => {
1015 return Err(
1016 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
1017 );
1018 }
1019 };
1020 if !cold.eq_ignore_ascii_case("cold") {
1021 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
1022 }
1023 self.advance();
1024 let next = self.peek().clone();
1025 let segments = match next {
1026 Token::Ident(s) | Token::QuotedIdent(s) => s,
1027 _ => {
1028 return Err(self.err(format!(
1029 "expected SEGMENTS after COMPACT COLD, got {:?}",
1030 self.peek()
1031 )));
1032 }
1033 };
1034 if !segments.eq_ignore_ascii_case("segments") {
1035 return Err(self.err(format!(
1036 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
1037 )));
1038 }
1039 self.advance();
1040 Ok(Statement::CompactColdSegments)
1041 }
1042 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
1049 self.advance();
1050 self.parse_merge_after_keyword()
1051 }
1052 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
1053 self.advance();
1054 let target = match self.peek() {
1055 Token::Eof | Token::Semicolon => None,
1056 Token::Ident(_) | Token::QuotedIdent(_) => {
1057 Some(self.expect_ident_like()?)
1058 }
1059 other => {
1060 return Err(self.err(format!(
1061 "expected table name or end of statement after ANALYZE, got {other:?}"
1062 )));
1063 }
1064 };
1065 Ok(Statement::Analyze(target))
1066 }
1067 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
1073 self.advance();
1074 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"))
1079 {
1080 self.advance();
1081 }
1082 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
1087 {
1088 self.advance();
1089 if matches!(
1091 self.peek(),
1092 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1093 ) {
1094 self.advance();
1095 }
1096 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
1098 {
1099 self.advance();
1100 if matches!(
1101 self.peek(),
1102 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1103 ) {
1104 self.advance();
1105 }
1106 }
1107 return Ok(Statement::Empty);
1108 }
1109 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
1119 {
1120 self.advance(); match self.peek().clone() {
1122 Token::Default => {
1123 self.advance();
1124 }
1125 Token::String(_)
1126 | Token::Ident(_)
1127 | Token::QuotedIdent(_) => {
1128 self.advance();
1129 }
1130 other => {
1131 return Err(self.err(alloc::format!(
1132 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
1133 )));
1134 }
1135 }
1136 return Ok(Statement::Empty);
1137 }
1138 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
1141 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
1142 {
1143 self.advance(); self.advance(); if matches!(
1146 self.peek(),
1147 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1148 ) {
1149 self.advance();
1150 }
1151 return Ok(Statement::Empty);
1152 }
1153 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
1158 loop {
1159 let lhs = match self.peek().clone() {
1160 Token::SessionVar(s) => {
1161 self.advance();
1162 s
1163 }
1164 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
1165 other => {
1166 return Err(self.err(format!(
1167 "expected parameter name after SET, got {other:?}"
1168 )));
1169 }
1170 };
1171 match self.peek() {
1173 Token::Eq => {
1174 self.advance();
1175 }
1176 Token::To => {
1177 self.advance();
1178 }
1179 other => {
1180 return Err(self.err(format!(
1181 "expected `=` or TO after SET {lhs}, got {other:?}"
1182 )));
1183 }
1184 }
1185 let value = self.parse_set_value()?;
1186 pairs.push((lhs, value));
1187 if matches!(self.peek(), Token::Comma) {
1188 self.advance();
1189 continue;
1190 }
1191 break;
1192 }
1193 if pairs.len() == 1 {
1194 let (name, value) = pairs.into_iter().next().unwrap();
1195 Ok(Statement::SetParameter { name, value })
1196 } else {
1197 Ok(Statement::SetParameterList(pairs))
1198 }
1199 }
1200 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1202 self.advance();
1203 match self.peek().clone() {
1204 Token::All => {
1205 self.advance();
1206 Ok(Statement::ResetParameter(None))
1207 }
1208 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1209 self.advance();
1210 Ok(Statement::ResetParameter(None))
1211 }
1212 _ => {
1213 let name = self.parse_set_param_name()?;
1214 Ok(Statement::ResetParameter(Some(name)))
1215 }
1216 }
1217 }
1218 other => Err(self.err(format!(
1219 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1220 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1221 ))),
1222 }
1223 }
1224
1225 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1226 debug_assert!(matches!(self.peek(), Token::Create));
1227 self.advance();
1228 match self.peek() {
1229 Token::Table => self.parse_create_table_stmt_after_create(),
1230 Token::Index => self.parse_create_index_stmt_after_create(false),
1231 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1238 self.advance();
1239 if !matches!(self.peek(), Token::Index) {
1240 return Err(self.err(alloc::format!(
1241 "expected INDEX after CREATE UNIQUE, got {:?}",
1242 self.peek()
1243 )));
1244 }
1245 self.parse_create_index_stmt_after_create(true)
1246 }
1247 Token::Publication => {
1248 self.advance();
1249 self.parse_create_publication_after_keyword()
1250 }
1251 Token::Subscription => {
1252 self.advance();
1253 self.parse_create_subscription_after_keyword()
1254 }
1255 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1259 self.advance();
1260 self.parse_create_user_after_keyword()
1261 }
1262 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1266 self.advance();
1267 self.parse_create_extension_after_keyword()
1268 }
1269 Token::Or => {
1275 self.advance();
1276 let next = self.peek();
1277 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1278 return Err(self.err(alloc::format!(
1279 "expected REPLACE after CREATE OR, got {next:?}"
1280 )));
1281 };
1282 if !s2.eq_ignore_ascii_case("replace") {
1283 return Err(self.err(alloc::format!(
1284 "expected REPLACE after CREATE OR, got {s2:?}"
1285 )));
1286 }
1287 self.advance();
1288 self.parse_create_function_or_trigger_after_or_replace(true)
1289 }
1290 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1291 self.advance();
1292 self.parse_create_function_after_keyword(false)
1293 }
1294 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1295 self.advance();
1296 self.parse_create_trigger_after_keyword(false)
1297 }
1298 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1300 self.advance();
1301 self.parse_create_sequence_after_keyword(false)
1302 }
1303 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1305 self.advance();
1306 self.parse_create_view_after_keyword(false, false, false)
1307 }
1308 Token::Ident(s) | Token::QuotedIdent(s)
1322 if s.eq_ignore_ascii_case("algorithm")
1323 || s.eq_ignore_ascii_case("definer")
1324 || s.eq_ignore_ascii_case("sql") =>
1325 {
1326 self.consume_mysql_view_prefix()?;
1327 let next = self.peek().clone();
1332 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1333 if s2.eq_ignore_ascii_case("view"))
1334 {
1335 self.advance();
1336 self.parse_create_view_after_keyword(false, false, false)
1337 } else {
1338 Err(self.err(alloc::format!(
1339 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1340 )))
1341 }
1342 }
1343 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1345 self.advance();
1346 self.parse_create_type_after_keyword()
1347 }
1348 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1351 self.advance();
1352 self.parse_create_domain_after_keyword()
1353 }
1354 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1358 self.advance();
1359 let if_not_exists = self.parse_if_not_exists();
1360 let name = self.expect_ident_like()?;
1361 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1364 if s.eq_ignore_ascii_case("authorization"))
1365 {
1366 self.advance();
1367 let _ = self.expect_ident_like()?;
1368 }
1369 Ok(Statement::CreateSchema { name, if_not_exists })
1370 }
1371 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1373 self.advance();
1374 let next = self.peek().clone();
1375 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1376 {
1377 self.advance();
1378 self.parse_create_materialized_view_after_keyword()
1379 } else {
1380 Err(self.err(alloc::format!(
1381 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1382 )))
1383 }
1384 }
1385 Token::Ident(s) | Token::QuotedIdent(s)
1386 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1387 {
1388 self.advance();
1389 let next = self.peek().clone();
1391 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1392 {
1393 self.advance();
1394 self.parse_create_sequence_after_keyword(true)
1395 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1396 {
1397 self.advance();
1398 self.parse_create_view_after_keyword(false, false, true)
1399 } else {
1400 self.consume_until_statement_boundary();
1402 Ok(Statement::Empty)
1403 }
1404 }
1405 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1415 self.consume_mysql_routine_body();
1416 Ok(Statement::Empty)
1417 }
1418 Token::Ident(s) | Token::QuotedIdent(s)
1429 if matches!(
1430 s.to_ascii_lowercase().as_str(),
1431 "database"
1432 | "role"
1433 | "policy"
1434 | "operator"
1435 | "cast"
1436 | "rule"
1437 | "aggregate"
1438 | "language"
1439 | "collation"
1440 | "conversion"
1441 | "statistics"
1449 | "event"
1450 | "foreign"
1451 ) =>
1452 {
1453 self.consume_until_statement_boundary();
1454 Ok(Statement::Empty)
1455 }
1456 other => Err(self.err(format!(
1457 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1458 ))),
1459 }
1460 }
1461
1462 fn parse_create_function_or_trigger_after_or_replace(
1467 &mut self,
1468 or_replace: bool,
1469 ) -> Result<Statement, ParseError> {
1470 let tok = self.peek();
1471 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1472 return Err(self.err(alloc::format!(
1473 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1474 )));
1475 };
1476 if s.eq_ignore_ascii_case("function") {
1477 self.advance();
1478 self.parse_create_function_after_keyword(or_replace)
1479 } else if s.eq_ignore_ascii_case("trigger") {
1480 self.advance();
1481 self.parse_create_trigger_after_keyword(or_replace)
1482 } else if s.eq_ignore_ascii_case("view") {
1483 self.advance();
1485 self.parse_create_view_after_keyword(or_replace, false, false)
1486 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1487 self.advance();
1489 let nxt = self.peek().clone();
1490 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1491 {
1492 self.advance();
1493 self.parse_create_view_after_keyword(or_replace, false, true)
1494 } else {
1495 Err(self.err(alloc::format!(
1496 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1497 )))
1498 }
1499 } else {
1500 Err(self.err(alloc::format!(
1501 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1502 )))
1503 }
1504 }
1505
1506 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1511 self.consume_if_not_exists();
1513 let name = self.expect_ident_like()?;
1514 loop {
1517 match self.peek() {
1518 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1519 self.advance();
1520 continue;
1521 }
1522 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1523 self.advance();
1524 let _ = self.expect_ident_like()?;
1525 continue;
1526 }
1527 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1528 self.advance();
1529 let _ = self.advance();
1531 continue;
1532 }
1533 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1534 self.advance();
1535 let _ = self.advance();
1536 continue;
1537 }
1538 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1539 self.advance();
1540 continue;
1541 }
1542 _ => break,
1543 }
1544 }
1545 Ok(Statement::CreateExtension(name))
1546 }
1547
1548 fn parse_create_function_after_keyword(
1560 &mut self,
1561 or_replace: bool,
1562 ) -> Result<Statement, ParseError> {
1563 let name = self.expect_ident_like()?;
1564 if !matches!(self.peek(), Token::LParen) {
1568 return Err(self.err(alloc::format!(
1569 "expected '(' after function name {name:?}, got {:?}",
1570 self.peek()
1571 )));
1572 }
1573 self.advance();
1574 let args = self.parse_function_arg_list()?;
1575 let tok = self.peek();
1577 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1578 return Err(self.err(alloc::format!(
1579 "expected RETURNS after function arg list, got {tok:?}"
1580 )));
1581 };
1582 if !s.eq_ignore_ascii_case("returns") {
1583 return Err(self.err(alloc::format!(
1584 "expected RETURNS after function arg list, got {s:?}"
1585 )));
1586 }
1587 self.advance();
1588 let returns = self.parse_function_return()?;
1589 let mut language: Option<String> = self.parse_optional_language()?;
1592 if !matches!(self.peek(), Token::As) {
1596 return Err(self.err(alloc::format!(
1597 "expected AS before function body, got {:?}",
1598 self.peek()
1599 )));
1600 }
1601 self.advance();
1602 let body_text = match self.peek() {
1603 Token::String(s) => {
1604 let body = s.clone();
1605 self.advance();
1606 body
1607 }
1608 other => {
1609 return Err(self.err(alloc::format!(
1610 "expected $$-quoted function body after AS, got {other:?}"
1611 )));
1612 }
1613 };
1614 if language.is_none() {
1616 language = self.parse_optional_language()?;
1617 }
1618 let language = language.unwrap_or_else(|| String::from("sql"));
1619 let body = if language.eq_ignore_ascii_case("plpgsql") {
1624 match parse_plpgsql_body(&body_text) {
1625 Ok(block) => FunctionBody::PlPgSql(block),
1626 Err(_) => FunctionBody::Raw(body_text),
1632 }
1633 } else {
1634 FunctionBody::Raw(body_text)
1635 };
1636 Ok(Statement::CreateFunction(CreateFunctionStatement {
1637 name,
1638 or_replace,
1639 args,
1640 returns,
1641 language,
1642 body,
1643 }))
1644 }
1645
1646 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1650 let mut args: Vec<FunctionArg> = Vec::new();
1651 if matches!(self.peek(), Token::RParen) {
1652 self.advance();
1653 return Ok(args);
1654 }
1655 loop {
1656 let mode = if matches!(self.peek(), Token::In) {
1659 self.advance();
1660 FunctionArgMode::In
1661 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1662 {
1663 self.advance();
1664 FunctionArgMode::Out
1665 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1666 {
1667 self.advance();
1668 FunctionArgMode::InOut
1669 } else {
1670 FunctionArgMode::In
1671 };
1672 let (name, ty_token) = {
1678 let first = self.expect_ident_like()?;
1679 match self.peek() {
1682 Token::Ident(_) | Token::QuotedIdent(_) => {
1683 let ty = self.expect_ident_like()?;
1684 (Some(first), ty)
1685 }
1686 _ => (None, first),
1687 }
1688 };
1689 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1691 Some(t) => FunctionArgType::Typed(t),
1692 None => FunctionArgType::Raw(ty_token),
1693 };
1694 args.push(FunctionArg { mode, name, ty });
1695 match self.peek() {
1696 Token::Comma => {
1697 self.advance();
1698 continue;
1699 }
1700 Token::RParen => {
1701 self.advance();
1702 return Ok(args);
1703 }
1704 other => {
1705 return Err(self.err(alloc::format!(
1706 "expected , or ) in function arg list, got {other:?}"
1707 )));
1708 }
1709 }
1710 }
1711 }
1712
1713 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1714 let ident = self.expect_ident_like()?;
1715 if ident.eq_ignore_ascii_case("trigger") {
1716 return Ok(FunctionReturn::Trigger);
1717 }
1718 if ident.eq_ignore_ascii_case("void") {
1719 return Ok(FunctionReturn::Void);
1720 }
1721 match map_type_ident_to_column_type_name(&ident) {
1722 Some(t) => Ok(FunctionReturn::Type(t)),
1723 None => Ok(FunctionReturn::Other(ident)),
1724 }
1725 }
1726
1727 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1728 match self.peek() {
1729 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1730 self.advance();
1731 let lang = self.expect_ident_like()?;
1732 Ok(Some(lang.to_ascii_lowercase()))
1733 }
1734 _ => Ok(None),
1735 }
1736 }
1737
1738 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1744 let name = self.expect_ident_like()?;
1745 if matches!(self.peek(), Token::As) {
1747 self.advance();
1748 }
1749 let base_type = self.parse_column_type_name()?;
1750 let mut default: Option<Expr> = None;
1751 let mut not_null = false;
1752 let mut checks: Vec<Expr> = Vec::new();
1753 loop {
1754 match self.peek() {
1755 Token::Default => {
1756 if default.is_some() {
1757 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1758 }
1759 self.advance();
1760 default = Some(self.parse_expr(0)?);
1761 }
1762 Token::Not => {
1763 self.advance();
1764 if !matches!(self.peek(), Token::Null) {
1765 return Err(self.err(alloc::format!(
1766 "expected NULL after NOT in DOMAIN, got {:?}",
1767 self.peek()
1768 )));
1769 }
1770 self.advance();
1771 not_null = true;
1772 }
1773 Token::Null => {
1774 self.advance();
1775 }
1779 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1780 self.advance();
1781 if !matches!(self.peek(), Token::LParen) {
1782 return Err(self.err(alloc::format!(
1783 "expected '(' after CHECK in DOMAIN, got {:?}",
1784 self.peek()
1785 )));
1786 }
1787 self.advance();
1788 let expr = self.parse_expr(0)?;
1789 if !matches!(self.peek(), Token::RParen) {
1790 return Err(self.err(alloc::format!(
1791 "expected ')' after CHECK expr, got {:?}",
1792 self.peek()
1793 )));
1794 }
1795 self.advance();
1796 checks.push(expr);
1797 }
1798 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1802 self.advance();
1803 let _ = self.expect_ident_like()?;
1804 }
1805 _ => break,
1806 }
1807 }
1808 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1809 name,
1810 base_type,
1811 default,
1812 not_null,
1813 checks,
1814 }))
1815 }
1816
1817 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1821 let name = self.expect_ident_like()?;
1822 if !matches!(self.peek(), Token::As) {
1824 return Err(self.err(alloc::format!(
1825 "expected AS after CREATE TYPE {name:?}, got {:?}",
1826 self.peek()
1827 )));
1828 }
1829 self.advance();
1830 if matches!(self.peek(), Token::LParen) {
1834 self.advance();
1835 let mut fields: Vec<(String, ColumnTypeName)> = Vec::new();
1836 loop {
1837 let field_name = self.expect_ident_like()?;
1838 let field_type = self.parse_column_type_name()?;
1839 fields.push((field_name, field_type));
1840 if matches!(self.peek(), Token::Comma) {
1841 self.advance();
1842 continue;
1843 }
1844 if matches!(self.peek(), Token::RParen) {
1845 self.advance();
1846 break;
1847 }
1848 return Err(self.err(alloc::format!(
1849 "expected , or ) in composite field list, got {:?}",
1850 self.peek()
1851 )));
1852 }
1853 if fields.is_empty() {
1854 return Err(self.err("CREATE TYPE … AS (…) must declare at least one field".into()));
1855 }
1856 return Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1857 name,
1858 kind: crate::ast::TypeKind::Composite { fields },
1859 }));
1860 }
1861 let kind_ident = match self.peek().clone() {
1863 Token::Ident(s) | Token::QuotedIdent(s) => s,
1864 other => {
1865 return Err(self.err(alloc::format!(
1866 "expected ENUM or '(' after CREATE TYPE {name:?} AS, got {other:?}"
1867 )));
1868 }
1869 };
1870 if !kind_ident.eq_ignore_ascii_case("enum") {
1871 return Err(self.err(alloc::format!(
1872 "Phase 1.4 only supports ENUM or composite '(…)'; got {kind_ident:?}"
1873 )));
1874 }
1875 self.advance();
1876 if !matches!(self.peek(), Token::LParen) {
1877 return Err(self.err(alloc::format!(
1878 "expected '(' after ENUM, got {:?}",
1879 self.peek()
1880 )));
1881 }
1882 self.advance();
1883 let mut labels: Vec<String> = Vec::new();
1884 loop {
1885 match self.peek().clone() {
1886 Token::String(s) => {
1887 self.advance();
1888 labels.push(s);
1889 }
1890 other => {
1891 return Err(
1892 self.err(alloc::format!("expected enum label string, got {other:?}"))
1893 );
1894 }
1895 }
1896 if matches!(self.peek(), Token::Comma) {
1897 self.advance();
1898 continue;
1899 }
1900 if matches!(self.peek(), Token::RParen) {
1901 self.advance();
1902 break;
1903 }
1904 return Err(self.err(alloc::format!(
1905 "expected , or ) in ENUM label list, got {:?}",
1906 self.peek()
1907 )));
1908 }
1909 if labels.is_empty() {
1910 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
1911 }
1912 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1913 name,
1914 kind: crate::ast::TypeKind::Enum { labels },
1915 }))
1916 }
1917
1918 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
1923 let if_not_exists = self.parse_if_not_exists();
1924 let name = self.expect_ident_like()?;
1925 let mut columns: Vec<String> = Vec::new();
1926 if matches!(self.peek(), Token::LParen) {
1927 self.advance();
1928 loop {
1929 let c = self.expect_ident_like()?;
1930 columns.push(c);
1931 if matches!(self.peek(), Token::Comma) {
1932 self.advance();
1933 continue;
1934 }
1935 if matches!(self.peek(), Token::RParen) {
1936 self.advance();
1937 break;
1938 }
1939 return Err(self.err(alloc::format!(
1940 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
1941 self.peek()
1942 )));
1943 }
1944 }
1945 if !matches!(self.peek(), Token::As) {
1946 return Err(self.err(alloc::format!(
1947 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
1948 self.peek()
1949 )));
1950 }
1951 self.advance();
1952 let body_stmt = self.parse_select_stmt()?;
1953 let Statement::Select(body) = body_stmt else {
1954 return Err(self.err(alloc::format!(
1955 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
1956 )));
1957 };
1958 let with_data = self.parse_optional_with_data(true)?;
1960 Ok(Statement::CreateMaterializedView(
1961 crate::ast::CreateMaterializedViewStatement {
1962 name,
1963 if_not_exists,
1964 columns,
1965 body,
1966 with_data,
1967 },
1968 ))
1969 }
1970
1971 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
1976 let save = self.pos;
1977 let is_with = match self.peek() {
1979 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
1980 _ => false,
1981 };
1982 if !is_with {
1983 return Ok(default_when_absent);
1984 }
1985 self.advance();
1986 let mut with_data = true;
1988 let is_no = match self.peek() {
1989 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
1990 _ => false,
1991 };
1992 if is_no {
1993 self.advance();
1994 with_data = false;
1995 }
1996 let is_data = match self.peek() {
1998 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
1999 _ => false,
2000 };
2001 if is_data {
2002 self.advance();
2003 Ok(with_data)
2004 } else {
2005 self.pos = save;
2008 Ok(default_when_absent)
2009 }
2010 }
2011
2012 fn parse_create_view_after_keyword(
2017 &mut self,
2018 or_replace: bool,
2019 _materialized_unused: bool,
2020 temporary: bool,
2021 ) -> Result<Statement, ParseError> {
2022 let if_not_exists = self.parse_if_not_exists();
2023 let name = self.expect_ident_like()?;
2024 let mut columns: Vec<String> = Vec::new();
2026 if matches!(self.peek(), Token::LParen) {
2027 self.advance();
2028 loop {
2029 let c = self.expect_ident_like()?;
2030 columns.push(c);
2031 if matches!(self.peek(), Token::Comma) {
2032 self.advance();
2033 continue;
2034 }
2035 if matches!(self.peek(), Token::RParen) {
2036 self.advance();
2037 break;
2038 }
2039 return Err(self.err(alloc::format!(
2040 "expected , or ) in VIEW column list, got {:?}",
2041 self.peek()
2042 )));
2043 }
2044 }
2045 if !matches!(self.peek(), Token::As) {
2047 return Err(self.err(alloc::format!(
2048 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
2049 self.peek()
2050 )));
2051 }
2052 self.advance();
2053 let body_stmt = self.parse_select_stmt()?;
2055 let Statement::Select(body) = body_stmt else {
2056 return Err(self.err(alloc::format!(
2057 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
2058 )));
2059 };
2060 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
2061 name,
2062 or_replace,
2063 if_not_exists,
2064 temporary,
2065 columns,
2066 body,
2067 }))
2068 }
2069
2070 fn parse_create_sequence_after_keyword(
2074 &mut self,
2075 temporary: bool,
2076 ) -> Result<Statement, ParseError> {
2077 let if_not_exists = self.parse_if_not_exists();
2078 let name = self.expect_ident_like()?;
2079 let data_type = if matches!(self.peek(), Token::As) {
2081 self.advance();
2082 Some(self.parse_sequence_data_type()?)
2083 } else {
2084 None
2085 };
2086 let options = self.parse_sequence_options(false)?;
2087 Ok(Statement::CreateSequence(
2088 crate::ast::CreateSequenceStatement {
2089 name,
2090 if_not_exists,
2091 temporary,
2092 data_type,
2093 options,
2094 },
2095 ))
2096 }
2097
2098 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
2101 let if_exists = self.parse_if_exists();
2102 let name = self.expect_ident_like()?;
2103 let options = self.parse_sequence_options(true)?;
2104 Ok(Statement::AlterSequence(
2105 crate::ast::AlterSequenceStatement {
2106 name,
2107 if_exists,
2108 options,
2109 },
2110 ))
2111 }
2112
2113 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
2114 let kw = self.expect_ident_like()?;
2115 match kw.to_ascii_lowercase().as_str() {
2116 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
2117 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
2118 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
2119 other => Err(self.err(alloc::format!(
2120 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
2121 ))),
2122 }
2123 }
2124
2125 fn parse_sequence_options(
2126 &mut self,
2127 allow_restart: bool,
2128 ) -> Result<crate::ast::SequenceOptions, ParseError> {
2129 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
2130 let mut opts = SequenceOptions::default();
2131 #[allow(clippy::while_let_loop)]
2132 loop {
2133 let kw_lc = match self.peek() {
2136 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
2137 _ => break,
2138 };
2139 match kw_lc.as_str() {
2140 "increment" => {
2141 self.advance();
2142 if matches!(self.peek(), Token::By) {
2144 self.advance();
2145 }
2146 opts.increment = Some(self.expect_signed_int()?);
2147 }
2148 "minvalue" => {
2149 self.advance();
2150 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
2151 }
2152 "maxvalue" => {
2153 self.advance();
2154 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
2155 }
2156 "no" => {
2157 self.advance();
2158 let what = self.expect_ident_like()?;
2159 match what.to_ascii_lowercase().as_str() {
2160 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
2161 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
2162 "cycle" => opts.cycle = Some(false),
2163 other => {
2164 return Err(self.err(alloc::format!(
2165 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
2166 )));
2167 }
2168 }
2169 }
2170 "start" => {
2171 self.advance();
2172 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2174 if s.eq_ignore_ascii_case("with"))
2175 {
2176 self.advance();
2177 }
2178 opts.start = Some(self.expect_signed_int()?);
2179 }
2180 "restart" if allow_restart => {
2181 self.advance();
2182 let mut with_val: Option<i64> = None;
2184 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2185 if s.eq_ignore_ascii_case("with"))
2186 {
2187 self.advance();
2188 with_val = Some(self.expect_signed_int()?);
2189 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
2190 with_val = Some(self.expect_signed_int()?);
2191 }
2192 opts.restart = Some(with_val);
2193 }
2194 "cache" => {
2195 self.advance();
2196 opts.cache = Some(self.expect_signed_int()?);
2197 }
2198 "cycle" => {
2199 self.advance();
2200 opts.cycle = Some(true);
2201 }
2202 "owned" => {
2203 self.advance();
2204 match self.peek() {
2206 Token::By => {
2207 self.advance();
2208 }
2209 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
2210 self.advance();
2211 }
2212 other => {
2213 return Err(
2214 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
2215 );
2216 }
2217 }
2218 let first = match self.advance() {
2222 Token::Ident(s) | Token::QuotedIdent(s) => s,
2223 other => {
2224 return Err(self.err(alloc::format!(
2225 "expected identifier or NONE after OWNED BY, got {other:?}"
2226 )));
2227 }
2228 };
2229 if first.eq_ignore_ascii_case("none") {
2230 opts.owned_by = Some(SequenceOwnedBy::None);
2231 } else if matches!(self.peek(), Token::Dot) {
2232 self.advance();
2233 let second = match self.advance() {
2234 Token::Ident(s) | Token::QuotedIdent(s) => s,
2235 other => {
2236 return Err(self.err(alloc::format!(
2237 "expected column name after OWNED BY {first}., got {other:?}"
2238 )));
2239 }
2240 };
2241 if matches!(self.peek(), Token::Dot) {
2250 self.advance();
2251 let third = match self.advance() {
2252 Token::Ident(s) | Token::QuotedIdent(s) => s,
2253 other => {
2254 return Err(self.err(alloc::format!(
2255 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2256 )));
2257 }
2258 };
2259 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2261 table: second,
2262 column: third,
2263 });
2264 } else {
2265 opts.owned_by = Some(SequenceOwnedBy::Column {
2266 table: first,
2267 column: second,
2268 });
2269 }
2270 } else {
2271 return Err(self.err(alloc::format!(
2272 "expected table.column or NONE after OWNED BY, got {first:?}"
2273 )));
2274 }
2275 }
2276 _ => break,
2277 }
2278 }
2279 Ok(opts)
2280 }
2281
2282 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2283 let neg = if matches!(self.peek(), Token::Minus) {
2284 self.advance();
2285 true
2286 } else {
2287 false
2288 };
2289 match self.peek() {
2290 Token::Integer(n) => {
2291 let v = *n;
2292 self.advance();
2293 Ok(if neg { -v } else { v })
2294 }
2295 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2296 }
2297 }
2298
2299 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2309 loop {
2310 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2312 self.advance();
2313 self.consume_optional_initially_clause()?;
2314 continue;
2315 }
2316 if matches!(self.peek(), Token::Not) {
2318 let look = self.tokens.get(self.pos + 1);
2319 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2320 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2323 continue;
2324 }
2325 break;
2326 }
2327 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2332 self.consume_optional_initially_clause()?;
2333 continue;
2334 }
2335 break;
2336 }
2337 Ok(())
2338 }
2339
2340 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2344 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2345 return Ok(());
2346 }
2347 self.advance(); match self.advance() {
2349 Token::Ident(s)
2350 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2351 {
2352 Ok(())
2353 }
2354 other => Err(self.err(alloc::format!(
2355 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2356 ))),
2357 }
2358 }
2359
2360 fn consume_mysql_routine_body(&mut self) {
2378 let mut depth: i32 = 0;
2383 let mut started = false;
2384 loop {
2385 match self.peek().clone() {
2386 Token::Begin => {
2387 self.advance();
2388 depth += 1;
2389 started = true;
2390 }
2391 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2392 self.advance();
2393 if started {
2394 depth -= 1;
2395 if depth <= 0 {
2396 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2403 let is_inner_close = matches!(
2408 self.peek(),
2409 Token::Ident(s) | Token::QuotedIdent(s)
2410 if matches!(
2411 s.to_ascii_lowercase().as_str(),
2412 "if" | "loop" | "while" | "case" | "repeat"
2413 )
2414 );
2415 if is_inner_close {
2416 self.advance();
2417 depth += 1;
2418 continue;
2419 }
2420 }
2421 if matches!(self.peek(), Token::Semicolon) {
2423 self.advance();
2424 }
2425 return;
2426 }
2427 }
2428 }
2429 Token::Eof => return,
2430 _ => {
2431 self.advance();
2432 }
2433 }
2434 }
2435 }
2436
2437 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2451 loop {
2452 match self.peek().clone() {
2453 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2454 self.advance(); if matches!(self.peek(), Token::Eq) {
2458 self.advance();
2459 }
2460 if matches!(
2464 self.peek(),
2465 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2466 ) {
2467 self.advance();
2468 }
2469 }
2470 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2471 self.advance(); if matches!(self.peek(), Token::Eq) {
2473 self.advance();
2474 }
2475 match self.peek().clone() {
2478 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2479 self.advance();
2480 if matches!(self.peek(), Token::At) {
2482 self.advance();
2483 if matches!(
2484 self.peek(),
2485 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2486 ) {
2487 self.advance();
2488 }
2489 }
2490 }
2491 _ => {}
2492 }
2493 }
2494 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2495 let save = self.pos;
2500 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2502 if s2.eq_ignore_ascii_case("security"))
2503 {
2504 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2507 self.advance();
2508 }
2509 } else {
2510 self.pos = save;
2513 return Ok(());
2514 }
2515 }
2516 _ => return Ok(()),
2517 }
2518 }
2519 }
2520
2521 fn parse_if_not_exists(&mut self) -> bool {
2522 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2523 {
2524 let save = self.pos;
2525 self.advance();
2526 if matches!(self.peek(), Token::Not) {
2527 self.advance();
2528 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2529 {
2530 self.advance();
2531 return true;
2532 }
2533 }
2534 self.pos = save;
2535 }
2536 false
2537 }
2538
2539 fn parse_if_exists(&mut self) -> bool {
2540 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2541 {
2542 let save = self.pos;
2543 self.advance();
2544 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2545 {
2546 self.advance();
2547 return true;
2548 }
2549 self.pos = save;
2550 }
2551 false
2552 }
2553
2554 fn parse_create_trigger_after_keyword(
2558 &mut self,
2559 or_replace: bool,
2560 ) -> Result<Statement, ParseError> {
2561 let name = self.expect_ident_like()?;
2562 let timing = {
2563 let ident = self.expect_ident_like()?;
2564 if ident.eq_ignore_ascii_case("before") {
2565 TriggerTiming::Before
2566 } else if ident.eq_ignore_ascii_case("after") {
2567 TriggerTiming::After
2568 } else if ident.eq_ignore_ascii_case("instead") {
2569 let next = self.expect_ident_like()?;
2570 if !next.eq_ignore_ascii_case("of") {
2571 return Err(self.err(alloc::format!(
2572 "expected OF after INSTEAD in trigger timing, got {next:?}"
2573 )));
2574 }
2575 TriggerTiming::InsteadOf
2576 } else {
2577 return Err(self.err(alloc::format!(
2578 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2579 )));
2580 }
2581 };
2582 let mut events: Vec<TriggerEvent> = Vec::new();
2589 let mut update_columns: Vec<String> = Vec::new();
2590 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2591 events.push(first_ev);
2592 if !first_cols.is_empty() {
2593 update_columns = first_cols;
2594 }
2595 while matches!(self.peek(), Token::Or) {
2596 self.advance();
2597 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2598 events.push(ev);
2599 if !cols.is_empty() {
2600 if !update_columns.is_empty() {
2601 return Err(
2602 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2603 );
2604 }
2605 update_columns = cols;
2606 }
2607 }
2608 let tok = self.peek();
2610 let Token::On = tok else {
2611 return Err(self.err(alloc::format!(
2612 "expected ON after trigger events, got {tok:?}"
2613 )));
2614 };
2615 self.advance();
2616 let table = self.expect_ident_like()?;
2617 if !matches!(self.peek(), Token::For) {
2621 return Err(self.err(alloc::format!(
2622 "expected FOR EACH ROW / STATEMENT, got {:?}",
2623 self.peek()
2624 )));
2625 }
2626 self.advance();
2627 let for_each = {
2628 let e = self.expect_ident_like()?;
2629 if !e.eq_ignore_ascii_case("each") {
2630 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2631 }
2632 let unit = self.expect_ident_like()?;
2633 if unit.eq_ignore_ascii_case("row") {
2634 TriggerForEach::Row
2635 } else if unit.eq_ignore_ascii_case("statement") {
2636 TriggerForEach::Statement
2637 } else {
2638 return Err(self.err(alloc::format!(
2639 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2640 )));
2641 }
2642 };
2643 let exec = self.expect_ident_like()?;
2645 if !exec.eq_ignore_ascii_case("execute") {
2646 return Err(self.err(alloc::format!(
2647 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2648 )));
2649 }
2650 let fn_or_proc = self.expect_ident_like()?;
2651 if !(fn_or_proc.eq_ignore_ascii_case("function")
2652 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2653 {
2654 return Err(self.err(alloc::format!(
2655 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2656 )));
2657 }
2658 let function = self.expect_ident_like()?;
2659 if matches!(self.peek(), Token::LParen) {
2661 self.advance();
2662 if !matches!(self.peek(), Token::RParen) {
2663 return Err(self.err(alloc::format!(
2664 "v7.12.4 trigger function calls take no args; got {:?}",
2665 self.peek()
2666 )));
2667 }
2668 self.advance();
2669 }
2670 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2671 name,
2672 or_replace,
2673 timing,
2674 events,
2675 table,
2676 for_each,
2677 function,
2678 update_columns,
2679 }))
2680 }
2681
2682 fn parse_trigger_event_with_optional_of(
2686 &mut self,
2687 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2688 let ev = self.parse_trigger_event()?;
2689 if !matches!(ev, TriggerEvent::Update) {
2690 return Ok((ev, Vec::new()));
2691 }
2692 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2694 return Ok((ev, Vec::new()));
2695 }
2696 self.advance(); let mut cols: Vec<String> = Vec::new();
2698 loop {
2699 cols.push(self.expect_ident_like()?);
2700 if matches!(self.peek(), Token::Comma) {
2701 self.advance();
2702 continue;
2703 }
2704 break;
2705 }
2706 if cols.is_empty() {
2707 return Err(
2708 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2709 );
2710 }
2711 Ok((ev, cols))
2712 }
2713
2714 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2721 let declarations = if matches!(
2723 self.peek(),
2724 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2725 ) {
2726 self.advance();
2727 self.parse_plpgsql_declare_block()?
2728 } else {
2729 Vec::new()
2730 };
2731 if !matches!(self.peek(), Token::Begin) {
2736 return Err(self.err(alloc::format!(
2737 "expected BEGIN at start of plpgsql block, got {:?}",
2738 self.peek()
2739 )));
2740 }
2741 self.advance();
2742 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2743 Ok(PlPgSqlBlock {
2744 declarations,
2745 statements,
2746 })
2747 }
2748
2749 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2753 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2754 loop {
2755 if matches!(self.peek(), Token::Begin) {
2756 return Ok(out);
2757 }
2758 let name = self.expect_ident_like()?;
2759 let ty_token = self.expect_ident_like()?;
2760 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2761 Some(t) => FunctionArgType::Typed(t),
2762 None => FunctionArgType::Raw(ty_token),
2763 };
2764 let default = match self.peek() {
2765 Token::ColonEq => {
2766 self.advance();
2767 Some(self.parse_expr(0)?)
2768 }
2769 Token::Eq => {
2770 self.advance();
2774 Some(self.parse_expr(0)?)
2775 }
2776 _ => None,
2777 };
2778 if !matches!(self.peek(), Token::Semicolon) {
2780 return Err(self.err(alloc::format!(
2781 "expected ; after DECLARE entry for {name:?}, got {:?}",
2782 self.peek()
2783 )));
2784 }
2785 self.advance();
2786 out.push(PlPgSqlDeclare { name, ty, default });
2787 }
2788 }
2789
2790 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2795 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2796 loop {
2797 while matches!(self.peek(), Token::Semicolon) {
2799 self.advance();
2800 }
2801 if matches!(
2803 self.peek(),
2804 Token::Ident(s) | Token::QuotedIdent(s)
2805 if s.eq_ignore_ascii_case("end")
2806 || s.eq_ignore_ascii_case("else")
2807 || s.eq_ignore_ascii_case("elsif")
2808 || s.eq_ignore_ascii_case("elseif")
2809 ) {
2810 return Ok(statements);
2811 }
2812 let stmt = self.parse_plpgsql_stmt()?;
2815 statements.push(stmt);
2816 match self.peek() {
2817 Token::Semicolon => {
2818 self.advance();
2819 }
2820 Token::Ident(s) | Token::QuotedIdent(s)
2821 if s.eq_ignore_ascii_case("end")
2822 || s.eq_ignore_ascii_case("else")
2823 || s.eq_ignore_ascii_case("elsif")
2824 || s.eq_ignore_ascii_case("elseif") =>
2825 {
2826 }
2828 other => {
2829 return Err(self.err(alloc::format!(
2830 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2831 )));
2832 }
2833 }
2834 }
2835 }
2836
2837 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2838 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2840 {
2841 self.advance();
2842 return self.parse_plpgsql_return();
2843 }
2844 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2846 {
2847 self.advance();
2848 return self.parse_plpgsql_if();
2849 }
2850 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2852 {
2853 self.advance();
2854 return self.parse_plpgsql_raise();
2855 }
2856 if matches!(self.peek(), Token::Select)
2867 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2868 {
2869 return Ok(PlPgSqlStmt::SelectInto {
2870 var: var_name,
2871 body: Box::new(select_body),
2872 });
2873 }
2874 if matches!(self.peek(), Token::Insert)
2884 || matches!(self.peek(), Token::Select)
2885 || matches!(self.peek(), Token::Create)
2886 || matches!(self.peek(), Token::Drop)
2887 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2888 if s.eq_ignore_ascii_case("update")
2889 || s.eq_ignore_ascii_case("delete")
2890 || s.eq_ignore_ascii_case("alter"))
2891 {
2892 let stmt = self.parse_one_statement()?;
2893 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2894 }
2895 let target = self.parse_plpgsql_assign_target()?;
2898 match self.peek() {
2901 Token::ColonEq => {
2902 self.advance();
2903 }
2904 Token::Colon => {
2905 self.advance();
2906 if !matches!(self.peek(), Token::Eq) {
2907 return Err(self.err(alloc::format!(
2908 "expected := after plpgsql assign target, got `:` then {:?}",
2909 self.peek()
2910 )));
2911 }
2912 self.advance();
2913 }
2914 other => {
2915 return Err(self.err(alloc::format!(
2916 "expected := after plpgsql assign target, got {other:?}"
2917 )));
2918 }
2919 }
2920 let value = self.parse_expr(0)?;
2921 Ok(PlPgSqlStmt::Assign { target, value })
2922 }
2923
2924 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2927 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
2928 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
2929 loop {
2930 let cond = self.parse_expr(0)?;
2932 let then_kw = self.expect_ident_like()?;
2933 if !then_kw.eq_ignore_ascii_case("then") {
2934 return Err(self.err(alloc::format!(
2935 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
2936 )));
2937 }
2938 let body = self.parse_plpgsql_stmt_list_until_end()?;
2939 branches.push((cond, body));
2940 match self.peek() {
2942 Token::Ident(s) | Token::QuotedIdent(s)
2943 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
2944 {
2945 self.advance();
2946 continue;
2947 }
2948 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
2949 self.advance();
2950 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
2951 break;
2952 }
2953 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2954 break;
2955 }
2956 other => {
2957 return Err(self.err(alloc::format!(
2958 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
2959 )));
2960 }
2961 }
2962 }
2963 let end_kw = self.expect_ident_like()?;
2966 if !end_kw.eq_ignore_ascii_case("end") {
2967 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
2968 }
2969 let if_kw = self.expect_ident_like()?;
2970 if !if_kw.eq_ignore_ascii_case("if") {
2971 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
2972 }
2973 Ok(PlPgSqlStmt::If {
2974 branches,
2975 else_branch,
2976 })
2977 }
2978
2979 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2983 let lvl_ident = self.expect_ident_like()?;
2984 let level = match lvl_ident.to_ascii_lowercase().as_str() {
2985 "notice" => RaiseLevel::Notice,
2986 "warning" => RaiseLevel::Warning,
2987 "info" => RaiseLevel::Info,
2988 "log" => RaiseLevel::Log,
2989 "debug" => RaiseLevel::Debug,
2990 "exception" => RaiseLevel::Exception,
2991 other => {
2992 return Err(self.err(alloc::format!(
2993 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
2994 )));
2995 }
2996 };
2997 let Token::String(msg) = self.peek() else {
3001 return Err(self.err(alloc::format!(
3002 "expected RAISE message string, got {:?}",
3003 self.peek()
3004 )));
3005 };
3006 let message = msg.clone();
3007 self.advance();
3008 let mut args: Vec<Expr> = Vec::new();
3010 while matches!(self.peek(), Token::Comma) {
3011 self.advance();
3012 args.push(self.parse_expr(0)?);
3013 }
3014 Ok(PlPgSqlStmt::Raise {
3015 level,
3016 message,
3017 args,
3018 })
3019 }
3020
3021 #[allow(clippy::too_many_lines)]
3029 fn try_parse_plpgsql_select_into(
3030 &mut self,
3031 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
3032 let start = self.pos;
3037 let mut into_pos: Option<usize> = None;
3038 let mut depth: i32 = 0;
3039 let mut i = start + 1;
3040 while i < self.tokens.len() {
3041 match &self.tokens[i] {
3042 Token::LParen => depth += 1,
3043 Token::RParen => depth -= 1,
3044 Token::Semicolon if depth == 0 => break,
3045 Token::Ident(s)
3046 if depth == 0
3047 && (s.eq_ignore_ascii_case("end")
3048 || s.eq_ignore_ascii_case("else")
3049 || s.eq_ignore_ascii_case("elsif")) =>
3050 {
3051 break;
3052 }
3053 Token::Into if depth == 0 => {
3054 into_pos = Some(i);
3055 break;
3056 }
3057 _ => {}
3058 }
3059 i += 1;
3060 }
3061 let Some(into_at) = into_pos else {
3062 return Ok(None);
3063 };
3064 let var = match self.tokens.get(into_at + 1) {
3068 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
3069 other => {
3070 return Err(self.err(alloc::format!(
3071 "expected variable name after SELECT … INTO, got {other:?}"
3072 )));
3073 }
3074 };
3075 let mut end = into_at + 2;
3078 let mut depth2: i32 = 0;
3079 while end < self.tokens.len() {
3080 match &self.tokens[end] {
3081 Token::LParen => depth2 += 1,
3082 Token::RParen => depth2 -= 1,
3083 Token::Semicolon if depth2 == 0 => break,
3084 Token::Ident(s)
3085 if depth2 == 0
3086 && (s.eq_ignore_ascii_case("end")
3087 || s.eq_ignore_ascii_case("else")
3088 || s.eq_ignore_ascii_case("elsif")) =>
3089 {
3090 break;
3091 }
3092 _ => {}
3093 }
3094 end += 1;
3095 }
3096 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
3101 for j in start..into_at {
3102 rebuilt.push(self.tokens[j].clone());
3103 }
3104 for j in (into_at + 2)..end {
3105 rebuilt.push(self.tokens[j].clone());
3106 }
3107 rebuilt.push(Token::Eof);
3108 let saved_pos = self.pos;
3109 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
3110 self.pos = 0;
3111 if !matches!(self.peek(), Token::Select) {
3113 self.tokens = saved_tokens;
3114 self.pos = saved_pos;
3115 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
3116 }
3117 let sel = self.parse_select_stmt();
3118 self.tokens = saved_tokens;
3119 self.pos = end;
3120 let sel = sel?;
3121 let Statement::Select(body) = sel else {
3122 return Err(self.err(alloc::format!(
3123 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
3124 )));
3125 };
3126 Ok(Some((body, var)))
3127 }
3128
3129 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
3130 let head = match self.advance() {
3144 Token::Ident(s) | Token::QuotedIdent(s) => s,
3145 other => {
3146 return Err(self.err(alloc::format!(
3147 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
3148 )));
3149 }
3150 };
3151 if matches!(self.peek(), Token::Dot) {
3152 self.advance();
3153 let col = self.expect_ident_like()?;
3154 if head.eq_ignore_ascii_case("new") {
3155 return Ok(AssignTarget::NewColumn(col));
3156 }
3157 if head.eq_ignore_ascii_case("old") {
3158 return Ok(AssignTarget::OldColumn(col));
3159 }
3160 return Err(self.err(alloc::format!(
3161 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
3162 got {head:?}.<col>"
3163 )));
3164 }
3165 Ok(AssignTarget::Local(head))
3166 }
3167
3168 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3169 match self.peek() {
3171 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
3172 self.advance();
3173 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
3174 }
3175 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
3176 self.advance();
3177 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
3178 }
3179 Token::Null => {
3180 self.advance();
3181 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3182 }
3183 Token::Semicolon => {
3186 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3187 }
3188 _ => {}
3189 }
3190 let e = self.parse_expr(0)?;
3192 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
3193 }
3194
3195 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
3196 if matches!(self.peek(), Token::Insert) {
3201 self.advance();
3202 return Ok(TriggerEvent::Insert);
3203 }
3204 match self.peek() {
3205 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3206 self.advance();
3207 Ok(TriggerEvent::Update)
3208 }
3209 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3210 self.advance();
3211 Ok(TriggerEvent::Delete)
3212 }
3213 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
3214 self.advance();
3215 Ok(TriggerEvent::Truncate)
3216 }
3217 other => Err(self.err(alloc::format!(
3218 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3219 ))),
3220 }
3221 }
3222
3223 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3230 let name = self.expect_ident_or_string()?;
3231 let scope = if matches!(self.peek(), Token::For) {
3234 self.advance();
3235 if matches!(self.peek(), Token::All) {
3236 self.advance();
3237 if !matches!(self.peek(), Token::Tables) {
3238 return Err(self.err(format!(
3239 "expected TABLES after FOR ALL, got {:?}",
3240 self.peek()
3241 )));
3242 }
3243 self.advance();
3244 if matches!(self.peek(), Token::Except) {
3245 self.advance();
3246 let tables = self.parse_publication_table_list()?;
3247 PublicationScope::AllTablesExcept(tables)
3248 } else {
3249 PublicationScope::AllTables
3250 }
3251 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3252 self.advance();
3255 let tables = self.parse_publication_table_list()?;
3256 PublicationScope::ForTables(tables)
3257 } else {
3258 return Err(self.err(format!(
3259 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3260 self.peek()
3261 )));
3262 }
3263 } else {
3264 PublicationScope::AllTables
3265 };
3266 Ok(Statement::CreatePublication(CreatePublicationStatement {
3267 name,
3268 scope,
3269 }))
3270 }
3271
3272 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3277 let first = self.expect_ident_like()?;
3278 let mut out = alloc::vec![first];
3279 while matches!(self.peek(), Token::Comma) {
3280 self.advance();
3281 out.push(self.expect_ident_like()?);
3282 }
3283 Ok(out)
3284 }
3285
3286 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3294 let name = self.expect_ident_or_string()?;
3295 if !matches!(self.peek(), Token::Connection) {
3296 return Err(self.err(format!(
3297 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3298 self.peek()
3299 )));
3300 }
3301 self.advance();
3302 let conn_str = self.expect_string_literal()?;
3303 if !matches!(self.peek(), Token::Publication) {
3304 return Err(self.err(format!(
3305 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3306 self.peek()
3307 )));
3308 }
3309 self.advance();
3310 let first = self.expect_ident_like()?;
3313 let mut publications = alloc::vec![first];
3314 while matches!(self.peek(), Token::Comma) {
3315 self.advance();
3316 publications.push(self.expect_ident_like()?);
3317 }
3318 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3319 name,
3320 conn_str,
3321 publications,
3322 }))
3323 }
3324
3325 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3332 let mut name = self.expect_ident_like()?;
3333 while matches!(self.peek(), Token::Dot) {
3334 self.advance();
3335 let next = self.expect_ident_like()?;
3336 name.push('.');
3337 name.push_str(&next);
3338 }
3339 Ok(name.to_ascii_lowercase())
3340 }
3341
3342 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3343 match self.advance() {
3344 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3345 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3346 Ok(crate::ast::SetValue::Default)
3347 }
3348 Token::Ident(s) | Token::QuotedIdent(s) => {
3349 let mut accum = s;
3350 while matches!(self.peek(), Token::Dot) {
3351 self.advance();
3352 let next = self.expect_ident_like()?;
3353 accum.push('.');
3354 accum.push_str(&next);
3355 }
3356 Ok(crate::ast::SetValue::Ident(accum))
3357 }
3358 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3359 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3360 Token::On => Ok(crate::ast::SetValue::Ident("on".to_string())),
3365 Token::True => Ok(crate::ast::SetValue::Ident("true".to_string())),
3366 Token::False => Ok(crate::ast::SetValue::Ident("false".to_string())),
3367 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3373 Token::Minus => match self.advance() {
3378 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3379 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3380 other => Err(self.err(format!(
3381 "expected numeric after `-` in SET value, got {other:?}"
3382 ))),
3383 },
3384 other => Err(self.err(format!(
3385 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3386 ))),
3387 }
3388 }
3389
3390 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3391 if !matches!(self.peek(), Token::For) {
3395 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3396 }
3397 self.advance();
3398 self.expect_keyword_ident("wal")?;
3399 self.expect_keyword_ident("position")?;
3400 let pos = self.expect_u64_literal()?;
3401 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3402 {
3403 self.advance();
3404 self.expect_keyword_ident("timeout")?;
3405 Some(self.expect_u64_literal()?)
3406 } else {
3407 None
3408 };
3409 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3410 }
3411
3412 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3416 match self.advance() {
3417 Token::Integer(n) if n >= 0 => Ok(n as u64),
3418 Token::Integer(n) => Err(ParseError {
3419 message: format!("expected non-negative integer, got {n}"),
3420 token_pos: self.pos.saturating_sub(1),
3421 }),
3422 other => Err(ParseError {
3423 message: format!("expected integer literal, got {other:?}"),
3424 token_pos: self.pos.saturating_sub(1),
3425 }),
3426 }
3427 }
3428
3429 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3433 let name = self.expect_ident_or_string()?;
3434 self.expect_keyword_ident("with")?;
3435 self.expect_keyword_ident("password")?;
3436 let password = self.expect_string_literal()?;
3437 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3438 && s.eq_ignore_ascii_case("role")
3439 {
3440 self.advance();
3441 self.expect_string_literal()?
3442 } else {
3443 "readonly".to_string()
3444 };
3445 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3446 name,
3447 password,
3448 role,
3449 }))
3450 }
3451
3452 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3455 let table = self.expect_ident_like()?;
3456 self.expect_keyword_ident("set")?;
3457 let mut assignments = Vec::new();
3458 loop {
3459 let col = self.expect_ident_like()?;
3460 if !matches!(self.peek(), Token::Eq) {
3461 return Err(self.err(format!(
3462 "expected `=` after column name in UPDATE SET, got {:?}",
3463 self.peek()
3464 )));
3465 }
3466 self.advance();
3467 let value = self.parse_expr(0)?;
3468 assignments.push((col, value));
3469 if matches!(self.peek(), Token::Comma) {
3470 self.advance();
3471 continue;
3472 }
3473 break;
3474 }
3475 let where_ = if matches!(self.peek(), Token::Where) {
3476 self.advance();
3477 Some(self.parse_expr(0)?)
3478 } else {
3479 None
3480 };
3481 let returning = self.parse_optional_returning()?;
3482 Ok(Statement::Update(crate::ast::UpdateStatement {
3483 ctes: Vec::new(),
3484 table,
3485 assignments,
3486 where_,
3487 returning,
3488 }))
3489 }
3490
3491 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3494 if !matches!(self.peek(), Token::From) {
3495 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3496 }
3497 self.advance();
3498 let table = self.expect_ident_like()?;
3499 let where_ = if matches!(self.peek(), Token::Where) {
3500 self.advance();
3501 Some(self.parse_expr(0)?)
3502 } else {
3503 None
3504 };
3505 let returning = self.parse_optional_returning()?;
3506 Ok(Statement::Delete(crate::ast::DeleteStatement {
3507 ctes: Vec::new(),
3508 table,
3509 where_,
3510 returning,
3511 }))
3512 }
3513
3514 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3524 let is_into_kw = matches!(self.peek(), Token::Into)
3526 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3527 if !is_into_kw {
3528 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3529 }
3530 self.advance();
3531 let target = self.expect_ident_like()?;
3532 let target_alias = match self.peek() {
3534 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3535 Some(self.expect_ident_like()?)
3536 }
3537 _ => None,
3538 };
3539 let is_using_kw = matches!(
3541 self.peek(),
3542 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3543 );
3544 if !is_using_kw {
3545 return Err(self.err(format!(
3546 "expected USING after MERGE INTO target, got {:?}",
3547 self.peek()
3548 )));
3549 }
3550 self.advance();
3551 let source = self.expect_ident_like()?;
3552 let source_alias = match self.peek() {
3553 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3554 Some(self.expect_ident_like()?)
3555 }
3556 _ => None,
3557 };
3558 if !matches!(self.peek(), Token::On) {
3560 return Err(self.err(format!(
3561 "expected ON after MERGE … USING source, got {:?}",
3562 self.peek()
3563 )));
3564 }
3565 self.advance();
3566 let on = self.parse_expr(0)?;
3567 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3569 loop {
3570 let is_when_kw = matches!(
3571 self.peek(),
3572 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3573 );
3574 if !is_when_kw {
3575 break;
3576 }
3577 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3580 self.advance();
3581 crate::ast::MergeMatched::NotMatched
3582 } else {
3583 crate::ast::MergeMatched::Matched
3584 };
3585 let is_matched_kw = matches!(
3586 self.peek(),
3587 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3588 );
3589 if !is_matched_kw {
3590 return Err(self.err(format!(
3591 "expected MATCHED in WHEN clause, got {:?}",
3592 self.peek()
3593 )));
3594 }
3595 self.advance();
3596 let condition = if matches!(self.peek(), Token::And) {
3598 self.advance();
3599 Some(self.parse_expr(0)?)
3600 } else {
3601 None
3602 };
3603 let is_then_kw = matches!(
3605 self.peek(),
3606 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3607 );
3608 if !is_then_kw {
3609 return Err(self.err(format!(
3610 "expected THEN in WHEN clause, got {:?}",
3611 self.peek()
3612 )));
3613 }
3614 self.advance();
3615 let action = match self.peek().clone() {
3617 Token::Insert => {
3618 self.advance();
3619 if !matches!(self.peek(), Token::LParen) {
3621 return Err(self.err(format!(
3622 "expected '(' after INSERT in MERGE, got {:?}",
3623 self.peek()
3624 )));
3625 }
3626 self.advance();
3627 let mut columns: Vec<String> = Vec::new();
3628 loop {
3629 columns.push(self.expect_ident_like()?);
3630 if matches!(self.peek(), Token::Comma) {
3631 self.advance();
3632 continue;
3633 }
3634 break;
3635 }
3636 if !matches!(self.peek(), Token::RParen) {
3637 return Err(self.err(format!(
3638 "expected ')' after INSERT column list, got {:?}",
3639 self.peek()
3640 )));
3641 }
3642 self.advance();
3643 if !matches!(self.peek(), Token::Values) {
3645 return Err(self.err(format!(
3646 "expected VALUES in MERGE INSERT, got {:?}",
3647 self.peek()
3648 )));
3649 }
3650 self.advance();
3651 if !matches!(self.peek(), Token::LParen) {
3652 return Err(self.err(format!(
3653 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3654 self.peek()
3655 )));
3656 }
3657 self.advance();
3658 let mut values: Vec<crate::ast::Expr> = Vec::new();
3659 loop {
3660 values.push(self.parse_expr(0)?);
3661 if matches!(self.peek(), Token::Comma) {
3662 self.advance();
3663 continue;
3664 }
3665 break;
3666 }
3667 if !matches!(self.peek(), Token::RParen) {
3668 return Err(self.err(format!(
3669 "expected ')' after MERGE INSERT values, got {:?}",
3670 self.peek()
3671 )));
3672 }
3673 self.advance();
3674 if columns.len() != values.len() {
3675 return Err(self.err(format!(
3676 "MERGE INSERT column count ({}) ≠ value count ({})",
3677 columns.len(),
3678 values.len()
3679 )));
3680 }
3681 crate::ast::MergeAction::Insert { columns, values }
3682 }
3683 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3684 self.advance();
3685 let is_set_kw = matches!(
3687 self.peek(),
3688 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3689 );
3690 if !is_set_kw {
3691 return Err(self.err(format!(
3692 "expected SET after UPDATE in MERGE, got {:?}",
3693 self.peek()
3694 )));
3695 }
3696 self.advance();
3697 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3698 loop {
3699 let col = self.expect_ident_like()?;
3700 if !matches!(self.peek(), Token::Eq) {
3701 return Err(self.err(format!(
3702 "expected '=' in MERGE UPDATE assignment, got {:?}",
3703 self.peek()
3704 )));
3705 }
3706 self.advance();
3707 let expr = self.parse_expr(0)?;
3708 assignments.push((col, expr));
3709 if matches!(self.peek(), Token::Comma) {
3710 self.advance();
3711 continue;
3712 }
3713 break;
3714 }
3715 crate::ast::MergeAction::Update { assignments }
3716 }
3717 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3718 self.advance();
3719 crate::ast::MergeAction::Delete
3720 }
3721 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3722 self.advance();
3723 let is_nothing_kw = matches!(
3724 self.peek(),
3725 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3726 );
3727 if !is_nothing_kw {
3728 return Err(self.err(format!(
3729 "expected NOTHING after DO in MERGE clause, got {:?}",
3730 self.peek()
3731 )));
3732 }
3733 self.advance();
3734 crate::ast::MergeAction::DoNothing
3735 }
3736 other => {
3737 return Err(self.err(format!(
3738 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3739 )));
3740 }
3741 };
3742 clauses.push(crate::ast::MergeWhenClause {
3743 matched,
3744 condition,
3745 action,
3746 });
3747 }
3748 if clauses.is_empty() {
3749 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3750 }
3751 Ok(Statement::Merge(crate::ast::MergeStatement {
3752 target,
3753 target_alias,
3754 source,
3755 source_alias,
3756 on,
3757 clauses,
3758 }))
3759 }
3760
3761 fn parse_optional_returning(
3766 &mut self,
3767 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3768 let is_returning_kw = matches!(
3769 self.peek(),
3770 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3771 );
3772 if !is_returning_kw {
3773 return Ok(None);
3774 }
3775 self.advance();
3776 let mut items = Vec::new();
3777 loop {
3778 items.push(self.parse_select_item()?);
3779 if matches!(self.peek(), Token::Comma) {
3780 self.advance();
3781 continue;
3782 }
3783 break;
3784 }
3785 Ok(Some(items))
3786 }
3787
3788 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
3796 match self.advance() {
3803 Token::Index => {}
3804 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
3805 Token::Table => {
3808 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3809 self.advance();
3810 }
3811 return self.parse_alter_table_after_keyword();
3812 }
3813 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
3814 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3815 self.advance();
3816 }
3817 return self.parse_alter_table_after_keyword();
3818 }
3819 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
3822 return self.parse_alter_sequence_after_keyword();
3823 }
3824 Token::Ident(s) | Token::QuotedIdent(s)
3830 if matches!(
3831 s.to_ascii_lowercase().as_str(),
3832 "view"
3833 | "function"
3834 | "type"
3835 | "domain"
3836 | "database"
3837 | "role"
3838 | "schema"
3839 | "owner"
3840 | "default"
3841 | "extension"
3842 | "materialized"
3843 | "policy"
3844 | "publication"
3845 | "subscription"
3846 ) =>
3847 {
3848 self.consume_until_statement_boundary();
3849 return Ok(Statement::Empty);
3850 }
3851 other => {
3852 return Err(self.err(format!(
3853 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
3854 after ALTER, got {other:?}"
3855 )));
3856 }
3857 }
3858 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3863 let next = self.tokens.get(self.pos + 1);
3864 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3865 self.advance();
3866 self.advance();
3867 true
3868 } else {
3869 false
3870 }
3871 } else {
3872 false
3873 };
3874 let name = self.expect_ident_like()?;
3875 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
3879 self.advance();
3880 if matches!(self.peek(), Token::To) {
3881 self.advance();
3882 } else {
3883 self.expect_keyword_ident("to")?;
3884 }
3885 let new = self.expect_ident_like()?;
3886 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3887 name,
3888 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
3889 }));
3890 }
3891 self.expect_keyword_ident("rebuild")?;
3893 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3895 self.advance();
3896 if !matches!(self.peek(), Token::LParen) {
3897 return Err(self.err(format!(
3898 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
3899 self.peek()
3900 )));
3901 }
3902 self.advance();
3903 self.expect_keyword_ident("encoding")?;
3904 if !matches!(self.peek(), Token::Eq) {
3905 return Err(self.err(format!(
3906 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
3907 self.peek()
3908 )));
3909 }
3910 self.advance();
3911 let enc_ident = match self.advance() {
3912 Token::Ident(s) | Token::QuotedIdent(s) => s,
3913 other => {
3914 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
3915 }
3916 };
3917 let enc = match enc_ident.to_ascii_lowercase().as_str() {
3918 "f32" => VecEncoding::F32,
3919 "sq8" => VecEncoding::Sq8,
3920 "half" => VecEncoding::F16,
3921 other => {
3922 return Err(self.err(format!(
3923 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
3924 )));
3925 }
3926 };
3927 if !matches!(self.peek(), Token::RParen) {
3928 return Err(self.err(format!(
3929 "expected ')' after encoding value, got {:?}",
3930 self.peek()
3931 )));
3932 }
3933 self.advance();
3934 Some(enc)
3935 } else {
3936 None
3937 };
3938 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3939 name,
3940 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
3941 }))
3942 }
3943
3944 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
3950 let table_name = self.expect_ident_like()?;
3951 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
3952 loop {
3953 let subaction = self.parse_alter_table_subaction()?;
3954 targets.extend(subaction);
3958 if matches!(self.peek(), Token::Comma) {
3959 self.advance();
3960 continue;
3961 }
3962 break;
3963 }
3964 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
3965 name: table_name,
3966 targets,
3967 }))
3968 }
3969
3970 fn parse_alter_table_subaction(
3974 &mut self,
3975 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
3976 match self.peek() {
3977 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
3978 self.advance();
3979 let setting = self.expect_ident_like()?;
3980 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
3981 return Err(self.err(alloc::format!(
3982 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
3983 )));
3984 }
3985 if !matches!(self.peek(), Token::Eq) {
3986 return Err(self.err(alloc::format!(
3987 "expected '=' after hot_tier_bytes, got {:?}",
3988 self.peek()
3989 )));
3990 }
3991 self.advance();
3992 let n = self.expect_u64_literal()?;
3993 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
3994 }
3995 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
3996 self.advance();
3997 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
4004 {
4005 let kind_pos = self.pos + 2;
4008 let kind = self.tokens.get(kind_pos).cloned();
4009 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
4010 {
4011 let fk = self.parse_table_level_fk()?;
4012 return Ok(alloc::vec![
4013 crate::ast::AlterTableTarget::AddForeignKey(fk)
4014 ]);
4015 }
4016 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
4017 {
4018 self.advance(); let _name = self.expect_ident_like()?;
4020 self.advance(); self.expect_keyword_ident("key")?;
4022 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4023 return Ok(alloc::vec![
4024 crate::ast::AlterTableTarget::AddTableConstraint(
4025 crate::ast::TableConstraint::PrimaryKey {
4026 name: None,
4027 columns: cols,
4028 }
4029 )
4030 ]);
4031 }
4032 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
4033 {
4034 self.advance(); let _name = self.expect_ident_like()?;
4036 let uc = self.parse_table_level_unique()?;
4042 return Ok(alloc::vec![
4043 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4044 ]);
4045 }
4046 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
4047 {
4048 self.advance(); let _name = self.expect_ident_like()?;
4050 self.advance(); if !matches!(self.peek(), Token::LParen) {
4052 return Err(self.err(alloc::format!(
4053 "expected '(' after CHECK, got {:?}", self.peek()
4054 )));
4055 }
4056 self.advance();
4057 let expr = self.parse_expr(0)?;
4058 if matches!(self.peek(), Token::RParen) {
4059 self.advance();
4060 }
4061 return Ok(alloc::vec![
4062 crate::ast::AlterTableTarget::AddTableConstraint(
4063 crate::ast::TableConstraint::Check { name: None, expr }
4064 )
4065 ]);
4066 }
4067 }
4070 let is_fk = matches!(
4071 self.peek(),
4072 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
4073 || s.eq_ignore_ascii_case("foreign")
4074 );
4075 if is_fk {
4076 let fk = self.parse_table_level_fk()?;
4077 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
4078 }
4079 match self.peek().clone() {
4082 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
4083 self.advance();
4084 self.expect_keyword_ident("key")?;
4085 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
4086 return Ok(alloc::vec![
4087 crate::ast::AlterTableTarget::AddTableConstraint(
4088 crate::ast::TableConstraint::PrimaryKey {
4089 name: None,
4090 columns: cols,
4091 }
4092 )
4093 ]);
4094 }
4095 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4096 let uc = self.parse_table_level_unique()?;
4098 return Ok(alloc::vec![
4099 crate::ast::AlterTableTarget::AddTableConstraint(uc)
4100 ]);
4101 }
4102 _ => {}
4103 }
4104 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4105 self.advance();
4106 }
4107 let mut if_not_exists = false;
4108 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4109 self.advance();
4110 if !matches!(self.peek(), Token::Not) {
4111 return Err(self.err(alloc::format!(
4112 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
4113 self.peek()
4114 )));
4115 }
4116 self.advance();
4117 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
4118 return Err(self.err(alloc::format!(
4119 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
4120 self.peek()
4121 )));
4122 }
4123 self.advance();
4124 if_not_exists = true;
4125 }
4126 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
4130 let col_name = column.name.clone();
4131 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
4132 column,
4133 if_not_exists,
4134 }];
4135 if let Some(mut fk) = col_level_fk {
4136 if fk.columns.is_empty() {
4137 fk.columns.push(col_name);
4138 }
4139 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
4140 }
4141 Ok(out)
4142 }
4143 Token::Drop => {
4144 self.advance();
4145 let subject = match self.peek() {
4152 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
4153 self.advance();
4154 "constraint"
4155 }
4156 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
4157 self.advance();
4158 "column"
4159 }
4160 Token::Ident(_) | Token::QuotedIdent(_) => "column",
4164 other => {
4165 return Err(self.err(alloc::format!(
4166 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
4167 )));
4168 }
4169 };
4170 let mut if_exists = false;
4171 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4172 let n1 = self.tokens.get(self.pos + 1);
4173 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4174 self.advance();
4175 self.advance();
4176 if_exists = true;
4177 }
4178 }
4179 let name = self.expect_ident_like()?;
4180 let mut cascade = false;
4181 if matches!(
4182 self.peek(),
4183 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
4184 || s.eq_ignore_ascii_case("restrict")
4185 ) {
4186 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
4187 {
4188 cascade = true;
4189 }
4190 self.advance();
4191 }
4192 if subject == "constraint" {
4193 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
4194 name,
4195 if_exists,
4196 }])
4197 } else {
4198 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
4199 column: name,
4200 if_exists,
4201 cascade,
4202 }])
4203 }
4204 }
4205 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
4206 self.advance();
4207 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4208 self.advance();
4209 }
4210 let col_name = self.expect_ident_like()?;
4211 match self.peek() {
4212 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
4213 self.advance();
4214 }
4215 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4223 let is_default_nextval =
4234 matches!(self.tokens.get(self.pos + 1), Some(Token::Default))
4235 && matches!(
4236 self.tokens.get(self.pos + 2),
4237 Some(Token::Ident(f)) if f.eq_ignore_ascii_case("nextval")
4238 );
4239 let seq_name = if is_default_nextval {
4242 self.scan_sequence_name_until_boundary()
4243 } else {
4244 self.consume_until_statement_boundary();
4245 None
4246 };
4247 if is_default_nextval {
4248 return Ok(alloc::vec![
4249 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4250 column: col_name,
4251 seq_name,
4252 }
4253 ]);
4254 }
4255 return Ok(Vec::new());
4261 }
4262 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4263 self.consume_until_statement_boundary();
4265 return Ok(Vec::new());
4266 }
4267 Token::Drop => {
4268 self.consume_until_statement_boundary();
4274 return Ok(Vec::new());
4275 }
4276 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4277 let is_generated = matches!(
4285 self.tokens.get(self.pos + 1),
4286 Some(Token::Ident(g)) if g.eq_ignore_ascii_case("generated")
4287 );
4288 if !is_generated {
4289 return Err(self.err(alloc::format!(
4290 "expected GENERATED after ALTER COLUMN {col_name} ADD, got {:?}",
4291 self.tokens.get(self.pos + 1)
4292 )));
4293 }
4294 let seq_name = self.scan_sequence_name_until_boundary();
4295 return Ok(alloc::vec![
4296 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4297 column: col_name,
4298 seq_name,
4299 }
4300 ]);
4301 }
4302 other => {
4303 return Err(self.err(alloc::format!(
4304 "expected TYPE / SET / DROP / ADD after ALTER COLUMN <name>, got {other:?}"
4305 )));
4306 }
4307 }
4308 let new_type = self.parse_column_type_name()?;
4309 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4310 {
4311 self.advance();
4312 Some(self.parse_expr(0)?)
4313 } else {
4314 None
4315 };
4316 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4317 column: col_name,
4318 new_type,
4319 using,
4320 }])
4321 }
4322 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4329 self.advance();
4330 if matches!(self.peek(), Token::To)
4335 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4336 {
4337 self.advance();
4338 let new = self.expect_ident_like()?;
4339 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4340 new,
4341 }]);
4342 }
4343 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4344 self.advance();
4345 }
4346 let old = self.expect_ident_like()?;
4347 if matches!(self.peek(), Token::To) {
4350 self.advance();
4351 } else {
4352 self.expect_keyword_ident("to")?;
4353 }
4354 let new = self.expect_ident_like()?;
4355 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4356 old,
4357 new,
4358 }])
4359 }
4360 Token::Ident(s)
4367 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4368 {
4369 let enabled = s.eq_ignore_ascii_case("enable");
4370 self.advance();
4371 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("keys")) {
4381 self.advance();
4382 return Ok(Vec::new());
4383 }
4384 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4385 return Err(self.err(alloc::format!(
4386 "expected TRIGGER after {}, got {:?}",
4387 if enabled { "ENABLE" } else { "DISABLE" },
4388 self.peek()
4389 )));
4390 }
4391 self.advance();
4392 let which = if matches!(self.peek(), Token::All)
4395 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4396 {
4397 self.advance();
4398 crate::ast::TriggerSelector::All
4399 } else {
4400 let name = self.expect_ident_like()?;
4401 crate::ast::TriggerSelector::Named(name)
4402 };
4403 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4404 which,
4405 enabled,
4406 }])
4407 }
4408 other => Err(self.err(alloc::format!(
4409 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4410 ))),
4411 }
4412 }
4413
4414 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4423 let schema = match self.tokens.get(self.pos) {
4425 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4426 _ => return None,
4427 };
4428 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4430 return None;
4431 }
4432 let tbl = match self.tokens.get(self.pos + 2)? {
4436 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4437 Token::Tables => "tables".to_string(),
4438 _ => return None,
4441 };
4442 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4446 ("__spg_info_", tbl.to_ascii_lowercase())
4447 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4448 let bare = tbl
4449 .to_ascii_lowercase()
4450 .strip_prefix("pg_")
4451 .map(alloc::string::String::from)
4452 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4453 ("__spg_pg_", bare)
4454 } else if schema.eq_ignore_ascii_case("mysql") {
4455 ("__spg_mysql_", tbl.to_ascii_lowercase())
4459 } else {
4460 return None;
4461 };
4462 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4466 }
4467
4468 fn try_peek_meta_bare(&mut self) -> Option<String> {
4475 const PG_META_TABLES: &[&str] = &[
4476 "pg_attribute",
4477 "pg_class",
4478 "pg_constraint",
4479 "pg_database",
4480 "pg_extension",
4481 "pg_index",
4482 "pg_indexes",
4483 "pg_matviews",
4484 "pg_namespace",
4485 "pg_proc",
4486 "pg_roles",
4487 "pg_settings",
4488 "pg_trigger",
4489 "pg_type",
4490 "pg_user",
4491 "pg_views",
4492 ];
4493 let name = match self.tokens.get(self.pos) {
4494 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4495 _ => return None,
4496 };
4497 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4500 return None;
4501 }
4502 if !PG_META_TABLES.contains(&name.as_str()) {
4503 return None;
4504 }
4505 self.advance();
4506 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4507 Some(alloc::format!("__spg_pg_{bare}"))
4508 }
4509
4510 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4512 match self.advance() {
4513 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4514 other => Err(ParseError {
4515 message: format!("expected {kw:?}, got {other:?}"),
4516 token_pos: self.pos.saturating_sub(1),
4517 }),
4518 }
4519 }
4520
4521 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4525 match self.advance() {
4526 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4527 other => Err(ParseError {
4528 message: format!("expected identifier or string, got {other:?}"),
4529 token_pos: self.pos.saturating_sub(1),
4530 }),
4531 }
4532 }
4533
4534 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4535 match self.advance() {
4536 Token::String(s) => Ok(s),
4537 other => Err(ParseError {
4538 message: format!("expected quoted string, got {other:?}"),
4539 token_pos: self.pos.saturating_sub(1),
4540 }),
4541 }
4542 }
4543
4544 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4545 self.enter_nested()?;
4549 let r = self.parse_select_stmt_inner();
4550 self.nest_depth -= 1;
4551 r
4552 }
4553
4554 fn parse_select_stmt_inner(&mut self) -> Result<Statement, ParseError> {
4555 let mut head = self.parse_bare_select()?;
4560 while matches!(self.peek(), Token::Union) {
4561 self.advance();
4562 let kind = if matches!(self.peek(), Token::All) {
4563 self.advance();
4564 UnionKind::All
4565 } else {
4566 UnionKind::Distinct
4567 };
4568 let peer = self.parse_bare_select()?;
4569 head.unions.push((kind, peer));
4570 }
4571 head.order_by = if matches!(self.peek(), Token::Order) {
4572 self.advance();
4573 if !matches!(self.peek(), Token::By) {
4574 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4575 }
4576 self.advance();
4577 let mut keys = Vec::new();
4580 loop {
4581 let expr = self.parse_expr(0)?;
4582 let desc = if matches!(self.peek(), Token::Desc) {
4583 self.advance();
4584 true
4585 } else if matches!(self.peek(), Token::Asc) {
4586 self.advance();
4587 false
4588 } else {
4589 false
4590 };
4591 let nulls_first = self.parse_optional_nulls_placement()?;
4593 keys.push(OrderBy {
4594 expr,
4595 desc,
4596 nulls_first,
4597 });
4598 if matches!(self.peek(), Token::Comma) {
4599 self.advance();
4600 } else {
4601 break;
4602 }
4603 }
4604 keys
4605 } else {
4606 Vec::new()
4607 };
4608 head.limit = if matches!(self.peek(), Token::Limit) {
4609 self.advance();
4610 if self.consume_limit_unbounded_sentinel() {
4617 None
4618 } else {
4619 Some(self.parse_limit_expr("LIMIT")?)
4620 }
4621 } else {
4622 None
4623 };
4624 head.offset = if matches!(self.peek(), Token::Offset) {
4625 self.advance();
4626 let off = self.parse_limit_expr("OFFSET")?;
4630 self.consume_optional_rows_keyword();
4631 Some(off)
4632 } else {
4633 None
4634 };
4635 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4641 {
4642 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4645 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4646 {
4647 self.advance();
4648 }
4649 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4652 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4653 {
4654 crate::ast::LimitExpr::Literal(1)
4656 } else {
4657 self.parse_limit_expr("FETCH FIRST")?
4658 };
4659 self.consume_optional_rows_keyword();
4661 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4667 if s.eq_ignore_ascii_case("only"))
4668 {
4669 self.advance();
4670 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4671 if s.eq_ignore_ascii_case("with"))
4672 {
4673 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4675 if s.eq_ignore_ascii_case("ties"))
4676 {
4677 self.advance();
4678 head.limit_with_ties = true;
4679 }
4680 }
4681 head.limit = Some(count);
4682 }
4683 self.consume_optional_for_lock_clauses();
4698 Ok(Statement::Select(head))
4699 }
4700
4701 fn consume_optional_for_lock_clauses(&mut self) {
4708 while matches!(self.peek(), Token::For) {
4709 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4713 if s.eq_ignore_ascii_case("no"))
4714 {
4715 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4719 if s.eq_ignore_ascii_case("key"))
4720 {
4721 self.advance(); }
4723 }
4724 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4726 if s.eq_ignore_ascii_case("key"))
4727 {
4728 self.advance(); }
4730 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4736 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4737 {
4738 self.advance();
4739 } else {
4740 return;
4745 }
4746 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4749 if s.eq_ignore_ascii_case("of"))
4750 {
4751 self.advance(); #[allow(clippy::while_let_loop)]
4753 loop {
4754 match self.peek() {
4755 Token::Ident(_) | Token::QuotedIdent(_) => {
4756 self.advance();
4757 if matches!(self.peek(), Token::Dot) {
4759 self.advance();
4760 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4761 self.advance();
4762 }
4763 }
4764 }
4765 _ => break,
4766 }
4767 if matches!(self.peek(), Token::Comma) {
4768 self.advance();
4769 } else {
4770 break;
4771 }
4772 }
4773 }
4774 match self.peek().clone() {
4776 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4777 self.advance();
4778 }
4779 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4780 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4782 if s.eq_ignore_ascii_case("locked"))
4783 {
4784 self.advance(); }
4786 }
4787 _ => {}
4788 }
4789 }
4791 }
4792
4793 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4802 if matches!(self.peek(), Token::Null) {
4803 self.advance();
4804 return true;
4805 }
4806 if matches!(self.peek(), Token::All) {
4807 self.advance();
4808 return true;
4809 }
4810 false
4811 }
4812
4813 fn consume_optional_rows_keyword(&mut self) {
4817 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4818 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4819 {
4820 self.advance();
4821 }
4822 }
4823
4824 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4825 match self.advance() {
4826 Token::Integer(n) if n >= 0 => u32::try_from(n)
4827 .map(crate::ast::LimitExpr::Literal)
4828 .map_err(|_| ParseError {
4829 message: alloc::format!("{label} value too large: {n}"),
4830 token_pos: self.pos.saturating_sub(1),
4831 }),
4832 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4833 other => Err(ParseError {
4834 message: alloc::format!(
4835 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4836 ),
4837 token_pos: self.pos.saturating_sub(1),
4838 }),
4839 }
4840 }
4841
4842 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4847 if !matches!(self.peek(), Token::Select) {
4848 return Err(self.err(format!(
4849 "expected SELECT to start a query block, got {:?}",
4850 self.peek()
4851 )));
4852 }
4853 self.advance();
4854 let distinct = if matches!(self.peek(), Token::Distinct) {
4855 self.advance();
4856 true
4857 } else {
4858 false
4859 };
4860 let items = self.parse_select_list()?;
4861 let from = if matches!(self.peek(), Token::From) {
4862 self.advance();
4863 Some(self.parse_from_clause()?)
4864 } else {
4865 None
4866 };
4867 let where_ = if matches!(self.peek(), Token::Where) {
4868 self.advance();
4869 Some(self.parse_expr(0)?)
4870 } else {
4871 None
4872 };
4873 let mut group_by_all = false;
4874 let group_by = if matches!(self.peek(), Token::Group) {
4875 self.advance();
4876 if !matches!(self.peek(), Token::By) {
4877 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4878 }
4879 self.advance();
4880 if matches!(self.peek(), Token::All) {
4883 self.advance();
4884 group_by_all = true;
4885 None
4886 } else {
4887 let mut groups = Vec::new();
4888 loop {
4889 groups.push(self.parse_expr(0)?);
4890 if matches!(self.peek(), Token::Comma) {
4891 self.advance();
4892 } else {
4893 break;
4894 }
4895 }
4896 Some(groups)
4897 }
4898 } else {
4899 None
4900 };
4901 let having = if matches!(self.peek(), Token::Having) {
4902 self.advance();
4903 Some(self.parse_expr(0)?)
4904 } else {
4905 None
4906 };
4907 Ok(SelectStatement {
4908 ctes: Vec::new(),
4909 distinct,
4910 items,
4911 from,
4912 where_,
4913 group_by,
4914 group_by_all,
4915 having,
4916 unions: Vec::new(),
4917 order_by: Vec::new(),
4918 limit: None,
4919 offset: None,
4920 limit_with_ties: false,
4921 })
4922 }
4923
4924 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4925 debug_assert!(matches!(self.peek(), Token::Table));
4927 self.advance();
4928 let if_not_exists = self.consume_if_not_exists();
4929 let name = self.expect_ident_like()?;
4930 if matches!(self.peek(), Token::Partition)
4935 && Self::tokens_match_ident_ci(self.tokens.get(self.pos + 1), "of")
4936 {
4937 self.advance(); self.advance(); let partition_of = self.parse_partition_of_tail()?;
4940 return Ok(Statement::CreateTable(CreateTableStatement {
4941 name,
4942 columns: Vec::new(),
4943 if_not_exists,
4944 foreign_keys: Vec::new(),
4945 table_constraints: Vec::new(),
4946 partition_by: None,
4947 partition_of: Some(partition_of),
4948 }));
4949 }
4950 if !matches!(self.peek(), Token::LParen) {
4951 return Err(self.err(format!(
4952 "expected '(' after table name, got {:?}",
4953 self.peek()
4954 )));
4955 }
4956 self.advance();
4957 let mut columns = Vec::new();
4958 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4959 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4960 loop {
4961 if self.peek_table_level_pk_start() {
4967 table_constraints.push(self.parse_table_level_primary_key()?);
4968 } else if self.peek_table_level_unique_start() {
4969 table_constraints.push(self.parse_table_level_unique()?);
4970 } else if self.peek_table_level_check_start() {
4971 table_constraints.push(self.parse_table_level_check()?);
4973 } else if self.peek_mysql_inline_key_start() {
4974 if let Some(uc) = self.parse_mysql_inline_key()? {
4980 table_constraints.push(uc);
4981 }
4982 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
4983 self.advance(); let _name = self.expect_ident_like()?;
4991 table_constraints.push(match kind {
4992 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
4993 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
4994 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
4995 });
4996 } else if self.peek_constraint_or_fk_start() {
4997 foreign_keys.push(self.parse_table_level_fk()?);
4998 } else {
4999 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
5000 if col.is_unique {
5004 table_constraints.push(crate::ast::TableConstraint::Unique {
5005 name: None,
5006 columns: alloc::vec![col.name.clone()],
5007 nulls_not_distinct: false,
5008 });
5009 }
5010 if let Some(check_expr) = col.check.clone() {
5011 table_constraints.push(crate::ast::TableConstraint::Check {
5012 name: None,
5013 expr: check_expr,
5014 });
5015 }
5016 columns.push(col);
5017 if let Some(fk) = col_level_fk {
5018 foreign_keys.push(fk);
5019 }
5020 }
5021 match self.peek() {
5022 Token::Comma => {
5023 self.advance();
5024 }
5025 Token::RParen => {
5026 self.advance();
5027 break;
5028 }
5029 other => {
5030 return Err(
5031 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
5032 );
5033 }
5034 }
5035 }
5036 if columns.is_empty() {
5037 return Err(self.err("CREATE TABLE requires at least one column".into()));
5038 }
5039 self.consume_mysql_table_options();
5046 let partition_by = if matches!(self.peek(), Token::Partition) {
5052 self.advance(); if !matches!(self.peek(), Token::By) {
5054 return Err(self.err(format!(
5055 "expected BY after PARTITION, got {:?}",
5056 self.peek()
5057 )));
5058 }
5059 self.advance();
5060 Some(self.parse_partition_by_tail()?)
5061 } else {
5062 None
5063 };
5064 Ok(Statement::CreateTable(CreateTableStatement {
5065 name,
5066 columns,
5067 if_not_exists,
5068 foreign_keys,
5069 table_constraints,
5070 partition_by,
5071 partition_of: None,
5072 }))
5073 }
5074
5075 fn tokens_match_ident_ci(t: Option<&Token>, want: &str) -> bool {
5081 matches!(t, Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case(want))
5082 }
5083
5084 fn parse_partition_by_tail(&mut self) -> Result<crate::ast::PartitionBySpec, ParseError> {
5086 use crate::ast::{PartitionBySpec, PartitionKindAst};
5087 let kind = match self.peek() {
5088 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("range") => {
5089 self.advance();
5090 PartitionKindAst::Range
5091 }
5092 other => {
5093 return Err(self.err(format!(
5094 "PARTITION BY: only RANGE is supported at v7.37.6-B, got {other:?}"
5095 )));
5096 }
5097 };
5098 if !matches!(self.peek(), Token::LParen) {
5099 return Err(self.err(format!(
5100 "expected '(' after PARTITION BY RANGE, got {:?}",
5101 self.peek()
5102 )));
5103 }
5104 self.advance();
5105 let mut key_columns = Vec::new();
5106 loop {
5107 key_columns.push(self.expect_ident_like()?);
5108 match self.peek() {
5109 Token::Comma => {
5110 self.advance();
5111 }
5112 Token::RParen => {
5113 self.advance();
5114 break;
5115 }
5116 other => {
5117 return Err(self.err(format!(
5118 "expected ',' or ')' in PARTITION BY key list, got {other:?}"
5119 )));
5120 }
5121 }
5122 }
5123 if key_columns.is_empty() {
5124 return Err(self.err("PARTITION BY RANGE requires at least one key column".to_string()));
5125 }
5126 Ok(PartitionBySpec { kind, key_columns })
5127 }
5128
5129 fn parse_partition_of_tail(&mut self) -> Result<crate::ast::PartitionOfSpec, ParseError> {
5134 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
5135 let parent_name = self.expect_ident_like()?;
5136 if matches!(self.peek(), Token::LParen) {
5142 return Err(self.err(
5143 "CREATE TABLE … PARTITION OF parent: explicit column list not supported \
5144 at v7.37.6-B; the child inherits its columns from the parent"
5145 .to_string(),
5146 ));
5147 }
5148 let bounds = match self.peek() {
5149 Token::Default => {
5150 self.advance();
5151 PartitionOfBoundsAst::Default
5152 }
5153 Token::For => {
5154 self.advance();
5155 if !matches!(self.peek(), Token::Values) {
5156 return Err(
5157 self.err(format!("expected VALUES after FOR, got {:?}", self.peek()))
5158 );
5159 }
5160 self.advance();
5161 if !matches!(self.peek(), Token::From) {
5162 return Err(self.err(format!(
5163 "expected FROM after FOR VALUES, got {:?}",
5164 self.peek()
5165 )));
5166 }
5167 self.advance();
5168 let lower = Box::new(self.parse_partition_bound_expr()?);
5169 if !matches!(self.peek(), Token::To) {
5170 return Err(self.err(format!(
5171 "expected TO after FROM (...), got {:?}",
5172 self.peek()
5173 )));
5174 }
5175 self.advance();
5176 let upper = Box::new(self.parse_partition_bound_expr()?);
5177 PartitionOfBoundsAst::Range { lower, upper }
5178 }
5179 other => {
5180 return Err(self.err(format!(
5181 "expected FOR VALUES or DEFAULT after PARTITION OF parent, got {other:?}"
5182 )));
5183 }
5184 };
5185 Ok(PartitionOfSpec {
5186 parent_name,
5187 bounds,
5188 })
5189 }
5190
5191 fn parse_partition_bound_expr(&mut self) -> Result<crate::ast::Expr, ParseError> {
5196 if !matches!(self.peek(), Token::LParen) {
5197 return Err(self.err(format!(
5198 "expected '(' before partition bound, got {:?}",
5199 self.peek()
5200 )));
5201 }
5202 self.advance();
5203 let expr = match self.peek() {
5204 Token::Ident(s) | Token::QuotedIdent(s)
5205 if s.eq_ignore_ascii_case("minvalue") || s.eq_ignore_ascii_case("maxvalue") =>
5206 {
5207 let name = s.to_ascii_uppercase();
5208 self.advance();
5209 crate::ast::Expr::FunctionCall {
5210 name,
5211 args: Vec::new(),
5212 }
5213 }
5214 _ => self.parse_expr(0)?,
5215 };
5216 if !matches!(self.peek(), Token::RParen) {
5217 return Err(self.err(format!(
5218 "expected ')' after partition bound, got {:?}",
5219 self.peek()
5220 )));
5221 }
5222 self.advance();
5223 Ok(expr)
5224 }
5225
5226 fn peek_mysql_inline_key_start(&self) -> bool {
5235 let cur = self.peek();
5236 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
5246 match self.tokens.get(skip) {
5249 Some(Token::LParen) => true,
5250 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
5251 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
5252 }
5253 _ => false,
5254 }
5255 };
5256 let is_key_or_index_tok = |t: &Token| -> bool {
5260 matches!(t, Token::Index)
5261 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
5262 };
5263 match cur {
5264 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
5265 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5266 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
5267 }
5268 Token::Ident(s)
5269 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
5270 {
5271 let nxt = self.tokens.get(self.pos + 1);
5272 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
5273 self.pos + 2
5274 } else {
5275 self.pos + 1
5276 };
5277 after_keyword_followed_by_paren_or_ident_paren(after_after)
5278 }
5279 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
5280 let nxt = self.tokens.get(self.pos + 1);
5281 if !nxt.is_some_and(is_key_or_index_tok) {
5282 return false;
5283 }
5284 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
5285 }
5286 _ => false,
5287 }
5288 }
5289
5290 fn parse_mysql_inline_key(
5299 &mut self,
5300 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
5301 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
5303 {
5304 self.advance();
5305 true
5306 } else {
5307 false
5308 };
5309 let mut is_fulltext = false;
5315 let mut is_spatial = false;
5316 if let Token::Ident(s) = self.peek().clone() {
5317 if s.eq_ignore_ascii_case("fulltext") {
5318 self.advance();
5319 is_fulltext = true;
5320 } else if s.eq_ignore_ascii_case("spatial") {
5321 self.advance();
5322 is_spatial = true;
5323 }
5324 }
5325 match self.peek() {
5328 Token::Index => {
5329 self.advance();
5330 }
5331 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
5332 self.advance();
5333 }
5334 other => {
5335 return Err(self.err(alloc::format!(
5336 "expected KEY/INDEX in inline index declaration, got {other:?}"
5337 )));
5338 }
5339 }
5340 let mut idx_name: Option<String> = None;
5345 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
5346 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
5347 {
5348 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
5349 idx_name = Some(s);
5350 }
5351 }
5352 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5354 self.advance();
5355 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5356 self.advance();
5357 }
5358 }
5359 if !matches!(self.peek(), Token::LParen) {
5361 return Err(self.err(alloc::format!(
5362 "expected '(' in inline KEY/INDEX, got {:?}",
5363 self.peek()
5364 )));
5365 }
5366 self.advance();
5367 let mut cols: Vec<String> = Vec::new();
5368 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5369 self.advance();
5370 cols.push(s);
5371 if matches!(self.peek(), Token::LParen) {
5373 let mut depth = 1usize;
5374 self.advance();
5375 while depth > 0 {
5376 match self.peek() {
5377 Token::LParen => depth += 1,
5378 Token::RParen => depth -= 1,
5379 Token::Eof => break,
5380 _ => {}
5381 }
5382 self.advance();
5383 }
5384 }
5385 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5387 || matches!(self.peek(), Token::Asc | Token::Desc)
5388 {
5389 self.advance();
5390 }
5391 if matches!(self.peek(), Token::Comma) {
5392 self.advance();
5393 continue;
5394 }
5395 break;
5396 }
5397 if matches!(self.peek(), Token::RParen) {
5398 self.advance();
5399 }
5400 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5403 self.advance();
5404 }
5405 if cols.is_empty() {
5406 return Ok(None);
5407 }
5408 if is_unique {
5409 Ok(Some(crate::ast::TableConstraint::Unique {
5415 name: idx_name,
5416 columns: cols,
5417 nulls_not_distinct: false,
5418 }))
5419 } else if is_fulltext {
5420 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5426 name: idx_name,
5427 columns: cols,
5428 }))
5429 } else if is_spatial {
5430 Ok(None)
5433 } else {
5434 Ok(Some(crate::ast::TableConstraint::Index {
5437 name: idx_name,
5438 columns: cols,
5439 }))
5440 }
5441 }
5442
5443 fn consume_mysql_table_options(&mut self) {
5448 loop {
5449 let name_lc = match self.peek().clone() {
5453 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5454 Token::Default => alloc::string::String::from("default"),
5455 _ => break,
5456 };
5457 let known = matches!(
5458 name_lc.as_str(),
5459 "engine"
5460 | "default"
5461 | "charset"
5462 | "collate"
5463 | "auto_increment"
5464 | "row_format"
5465 | "comment"
5466 | "pack_keys"
5467 | "stats_persistent"
5468 | "stats_auto_recalc"
5469 | "stats_sample_pages"
5470 | "key_block_size"
5471 | "tablespace"
5472 | "min_rows"
5473 | "max_rows"
5474 | "checksum"
5475 | "delay_key_write"
5476 | "insert_method"
5477 | "data"
5478 | "index"
5479 | "encryption"
5480 | "compression"
5481 );
5482 if !known {
5483 break;
5484 }
5485 self.advance(); if name_lc == "default" {
5489 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5490 self.advance();
5491 }
5492 }
5493 if matches!(self.peek(), Token::Eq) {
5494 self.advance();
5495 }
5496 match self.peek() {
5497 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5498 self.advance();
5499 }
5500 _ => {}
5501 }
5502 }
5503 }
5504
5505 fn peek_table_level_pk_start(&self) -> bool {
5510 let cur = self.peek();
5511 let nxt = self.tokens.get(self.pos + 1);
5512 let nxt2 = self.tokens.get(self.pos + 2);
5513 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5514 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5515 let is_lparen = matches!(nxt2, Some(Token::LParen));
5516 is_primary && is_key && is_lparen
5517 }
5518
5519 fn peek_table_level_unique_start(&self) -> bool {
5523 let cur = self.peek();
5524 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5525 if !is_unique {
5526 return false;
5527 }
5528 let n1 = self.tokens.get(self.pos + 1);
5529 if matches!(n1, Some(Token::LParen)) {
5531 return true;
5532 }
5533 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5535 if !is_nulls {
5536 return false;
5537 }
5538 let n2 = self.tokens.get(self.pos + 2);
5539 let n3 = self.tokens.get(self.pos + 3);
5540 let n4 = self.tokens.get(self.pos + 4);
5541 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5543 return true;
5544 }
5545 if matches!(n2, Some(Token::Not))
5547 && matches!(n3, Some(Token::Distinct))
5548 && matches!(n4, Some(Token::LParen))
5549 {
5550 return true;
5551 }
5552 false
5553 }
5554
5555 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5556 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5559 Ok(crate::ast::TableConstraint::PrimaryKey {
5560 name: None,
5561 columns,
5562 })
5563 }
5564
5565 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5566 self.advance(); let mut nulls_not_distinct = false;
5571 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5572 let n1 = self.tokens.get(self.pos + 1);
5573 let n2 = self.tokens.get(self.pos + 2);
5574 let is_not = matches!(n1, Some(Token::Not));
5575 let is_distinct = matches!(n2, Some(Token::Distinct));
5576 if is_not && is_distinct {
5577 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5581 } else if matches!(n1, Some(Token::Distinct)) {
5582 self.advance(); self.advance(); }
5585 }
5586 let columns = self.parse_paren_ident_list("UNIQUE")?;
5587 Ok(crate::ast::TableConstraint::Unique {
5588 name: None,
5589 columns,
5590 nulls_not_distinct,
5591 })
5592 }
5593
5594 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5598 self.advance(); if !matches!(self.peek(), Token::LParen) {
5600 return Err(self.err(alloc::format!(
5601 "expected '(' after CHECK, got {:?}",
5602 self.peek()
5603 )));
5604 }
5605 self.advance();
5606 let expr = self.parse_expr(0)?;
5607 if !matches!(self.peek(), Token::RParen) {
5608 return Err(self.err(alloc::format!(
5609 "expected ')' to close CHECK predicate, got {:?}",
5610 self.peek()
5611 )));
5612 }
5613 self.advance();
5614 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5615 }
5616
5617 fn peek_table_level_check_start(&self) -> bool {
5619 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5620 }
5621
5622 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5627 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5628 return None;
5629 }
5630 match self.tokens.get(self.pos + 2) {
5633 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5634 Some(NamedTableConstraintKind::Check)
5635 }
5636 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5637 Some(NamedTableConstraintKind::Unique)
5638 }
5639 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5640 Some(NamedTableConstraintKind::PrimaryKey)
5641 }
5642 _ => None,
5643 }
5644 }
5645
5646 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5647 if !matches!(self.peek(), Token::LParen) {
5648 return Err(self.err(alloc::format!(
5649 "expected '(' after {ctx}, got {:?}",
5650 self.peek()
5651 )));
5652 }
5653 self.advance();
5654 let mut out = Vec::new();
5655 loop {
5656 out.push(self.expect_ident_like()?);
5657 match self.peek() {
5658 Token::Comma => {
5659 self.advance();
5660 }
5661 Token::RParen => {
5662 self.advance();
5663 break;
5664 }
5665 other => {
5666 return Err(self.err(alloc::format!(
5667 "expected ',' or ')' in {ctx} list, got {other:?}"
5668 )));
5669 }
5670 }
5671 }
5672 if out.is_empty() {
5673 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5674 }
5675 Ok(out)
5676 }
5677
5678 fn peek_constraint_or_fk_start(&self) -> bool {
5683 let is_constraint_kw = matches!(
5684 self.peek(),
5685 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5686 );
5687 let is_foreign_kw = matches!(
5688 self.peek(),
5689 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5690 );
5691 is_constraint_kw || is_foreign_kw
5692 }
5693
5694 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5698 let mut name: Option<String> = None;
5699 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5700 self.advance();
5701 name = Some(self.expect_ident_like()?);
5702 }
5703 match self.advance() {
5705 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5706 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5707 }
5708 match self.advance() {
5710 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5711 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5712 }
5713 if !matches!(self.peek(), Token::LParen) {
5715 return Err(self.err(format!(
5716 "expected '(' after FOREIGN KEY, got {:?}",
5717 self.peek()
5718 )));
5719 }
5720 self.advance();
5721 let mut columns = Vec::new();
5722 loop {
5723 columns.push(self.expect_ident_like()?);
5724 match self.peek() {
5725 Token::Comma => {
5726 self.advance();
5727 }
5728 Token::RParen => {
5729 self.advance();
5730 break;
5731 }
5732 other => {
5733 return Err(self.err(format!(
5734 "expected ',' or ')' in FK column list, got {other:?}"
5735 )));
5736 }
5737 }
5738 }
5739 if columns.is_empty() {
5740 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5741 }
5742 let (parent_table, parent_columns, on_delete, on_update) =
5743 self.parse_references_tail(columns.len())?;
5744 Ok(ForeignKeyConstraint {
5745 name,
5746 columns,
5747 parent_table,
5748 parent_columns,
5749 on_delete,
5750 on_update,
5751 })
5752 }
5753
5754 fn parse_references_tail(
5759 &mut self,
5760 expected_arity: usize,
5761 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5762 match self.advance() {
5763 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5764 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5765 }
5766 let parent_table = self.expect_ident_like()?;
5767 let mut parent_columns: Vec<String> = Vec::new();
5768 if matches!(self.peek(), Token::LParen) {
5769 self.advance();
5770 loop {
5771 parent_columns.push(self.expect_ident_like()?);
5772 match self.peek() {
5773 Token::Comma => {
5774 self.advance();
5775 }
5776 Token::RParen => {
5777 self.advance();
5778 break;
5779 }
5780 other => {
5781 return Err(self.err(format!(
5782 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5783 )));
5784 }
5785 }
5786 }
5787 }
5788 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5789 return Err(self.err(format!(
5790 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5791 expected_arity,
5792 parent_columns.len()
5793 )));
5794 }
5795 let mut on_delete = FkAction::Restrict;
5808 let mut on_update = FkAction::Restrict;
5809 let mut seen_on_delete = false;
5810 let mut seen_on_update = false;
5811 loop {
5812 let before = self.pos;
5814 self.consume_optional_deferrable_clauses()?;
5815 if self.pos != before {
5816 continue;
5817 }
5818 if !matches!(self.peek(), Token::On) {
5820 break;
5821 }
5822 self.advance();
5823 let which = self.advance();
5824 let action = self.parse_fk_action()?;
5825 match which {
5826 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5827 if seen_on_delete {
5828 return Err(self.err("ON DELETE specified twice".into()));
5829 }
5830 seen_on_delete = true;
5831 on_delete = action;
5832 }
5833 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5834 if seen_on_update {
5835 return Err(self.err("ON UPDATE specified twice".into()));
5836 }
5837 seen_on_update = true;
5838 on_update = action;
5839 }
5840 other => {
5841 return Err(
5842 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5843 );
5844 }
5845 }
5846 }
5847 Ok((parent_table, parent_columns, on_delete, on_update))
5848 }
5849
5850 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5853 match self.advance() {
5854 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5855 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5856 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5857 Token::Null => Ok(FkAction::SetNull),
5858 Token::Default => Ok(FkAction::SetDefault),
5859 other => Err(self.err(format!(
5860 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5861 ))),
5862 },
5863 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5864 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5865 other => Err(self.err(format!(
5866 "expected ACTION after NO in FK action, got {other:?}"
5867 ))),
5868 },
5869 other => Err(self.err(format!(
5870 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5871 ))),
5872 }
5873 }
5874
5875 fn consume_if_not_exists(&mut self) -> bool {
5878 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5882 if !looks_like_if {
5883 return false;
5884 }
5885 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5888 return false;
5889 }
5890 if !matches!(
5891 self.tokens.get(self.pos + 2),
5892 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5893 ) {
5894 return false;
5895 }
5896 self.advance(); self.advance(); self.advance(); true
5900 }
5901
5902 fn consume_if_exists(&mut self) -> bool {
5906 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5907 if !looks_like_if {
5908 return false;
5909 }
5910 if !matches!(
5911 self.tokens.get(self.pos + 1),
5912 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5913 ) {
5914 return false;
5915 }
5916 self.advance(); self.advance(); true
5919 }
5920
5921 fn parse_optional_nulls_placement(&mut self) -> Result<Option<bool>, ParseError> {
5929 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5930 return Ok(None);
5931 }
5932 self.advance();
5933 match self.advance() {
5934 Token::Ident(s) if s.eq_ignore_ascii_case("first") => Ok(Some(true)),
5935 Token::Ident(s) if s.eq_ignore_ascii_case("last") => Ok(Some(false)),
5936 other => Err(self.err(alloc::format!(
5937 "expected FIRST or LAST after NULLS, got {other:?}"
5938 ))),
5939 }
5940 }
5941
5942 fn consume_optional_index_column_qualifiers(&mut self) {
5943 loop {
5944 match self.peek() {
5945 Token::Asc | Token::Desc => {
5946 self.advance();
5947 }
5948 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5949 let look = self.tokens.get(self.pos + 1);
5950 if matches!(
5951 look,
5952 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5953 || k.eq_ignore_ascii_case("last")
5954 ) {
5955 self.advance();
5956 self.advance();
5957 } else {
5958 break;
5959 }
5960 }
5961 _ => break,
5962 }
5963 }
5964 }
5965
5966 fn parse_create_index_stmt_after_create(
5967 &mut self,
5968 is_unique: bool,
5969 ) -> Result<Statement, ParseError> {
5970 debug_assert!(matches!(self.peek(), Token::Index));
5972 self.advance();
5973 let if_not_exists = self.consume_if_not_exists();
5974 let name = self.expect_ident_like()?;
5975 if !matches!(self.peek(), Token::On) {
5976 return Err(self.err(format!(
5977 "expected ON after CREATE INDEX <name>, got {:?}",
5978 self.peek()
5979 )));
5980 }
5981 self.advance();
5982 let table = self.expect_ident_like()?;
5983 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5988 self.advance();
5989 let m = self.expect_ident_like()?;
5990 match m.to_ascii_lowercase().as_str() {
5991 "hnsw" => IndexMethod::Hnsw,
5992 "btree" => IndexMethod::BTree,
5993 "brin" => IndexMethod::Brin,
5994 "gin" => IndexMethod::Gin,
5999 "gist" | "spgist" | "hash" => IndexMethod::BTree,
6008 "ivfflat" => IndexMethod::Hnsw,
6016 other => {
6017 return Err(self.err(alloc::format!(
6018 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
6019 )));
6020 }
6021 }
6022 } else {
6023 IndexMethod::BTree
6024 };
6025 if !matches!(self.peek(), Token::LParen) {
6026 return Err(self.err(format!(
6027 "expected '(' before indexed column, got {:?}",
6028 self.peek()
6029 )));
6030 }
6031 self.advance();
6032 let mut opclass: Option<String> = None;
6041 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
6042 Token::Ident(s) | Token::QuotedIdent(s)
6049 if matches!(
6050 self.tokens.get(self.pos + 1),
6051 Some(Token::RParen | Token::Comma)
6052 ) =>
6053 {
6054 self.advance();
6055 (s, None)
6056 }
6057 Token::Ident(s) | Token::QuotedIdent(s)
6071 if matches!(
6072 self.tokens.get(self.pos + 1),
6073 Some(Token::Ident(_) | Token::QuotedIdent(_))
6074 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
6075 && matches!(
6076 self.tokens.get(self.pos + 3),
6077 Some(Token::Ident(op) | Token::QuotedIdent(op))
6078 if is_vector_opclass_name(op)
6079 ) =>
6080 {
6081 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
6085 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6086 opclass = Some(op.to_ascii_lowercase());
6087 }
6088 (s, None)
6089 }
6090 Token::Ident(s) | Token::QuotedIdent(s)
6091 if matches!(
6092 self.tokens.get(self.pos + 1),
6093 Some(Token::Ident(op) | Token::QuotedIdent(op))
6094 if is_vector_opclass_name(op)
6095 ) =>
6096 {
6097 self.advance(); let op_tok = self.advance();
6101 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
6102 opclass = Some(op.to_ascii_lowercase());
6103 }
6104 (s, None)
6105 }
6106 Token::Ident(_) | Token::QuotedIdent(_) => {
6107 let key_expr = self.parse_expr(0)?;
6108 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6109 self.err("expression index key must reference at least one column".into())
6110 })?;
6111 (primary, Some(key_expr))
6112 }
6113 Token::LParen => {
6121 let key_expr = self.parse_expr(0)?;
6122 let primary = extract_first_column(&key_expr).ok_or_else(|| {
6123 self.err("expression index key must reference at least one column".into())
6124 })?;
6125 (primary, Some(key_expr))
6126 }
6127 other => {
6128 return Err(self.err(format!(
6129 "expected column ident or expression, got {other:?}"
6130 )));
6131 }
6132 };
6133 let mut extra_columns: Vec<String> = Vec::new();
6142 self.consume_optional_index_column_qualifiers();
6144 while matches!(self.peek(), Token::Comma) {
6145 self.advance();
6146 let extra = self.expect_ident_like()?;
6147 self.consume_optional_index_column_qualifiers();
6148 extra_columns.push(extra);
6149 }
6150 if !matches!(self.peek(), Token::RParen) {
6151 return Err(self.err(format!(
6152 "expected ')' after indexed column / expression, got {:?}",
6153 self.peek()
6154 )));
6155 }
6156 self.advance();
6157 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
6161 {
6162 self.advance();
6163 if !matches!(self.peek(), Token::LParen) {
6164 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
6165 }
6166 self.advance();
6167 let mut cols = Vec::new();
6168 loop {
6169 cols.push(self.expect_ident_like()?);
6170 match self.peek() {
6171 Token::Comma => {
6172 self.advance();
6173 }
6174 Token::RParen => {
6175 self.advance();
6176 break;
6177 }
6178 other => {
6179 return Err(self.err(format!(
6180 "expected ',' or ')' in INCLUDE list, got {other:?}"
6181 )));
6182 }
6183 }
6184 }
6185 cols
6186 } else {
6187 Vec::new()
6188 };
6189 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
6195 self.advance();
6196 if !matches!(self.peek(), Token::LParen) {
6197 return Err(self.err(format!(
6198 "expected '(' after WITH in CREATE INDEX, got {:?}",
6199 self.peek()
6200 )));
6201 }
6202 self.advance();
6203 loop {
6204 if matches!(self.peek(), Token::RParen) {
6205 self.advance();
6206 break;
6207 }
6208 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
6211 self.advance();
6212 let _ = self.advance(); }
6214 match self.peek() {
6215 Token::Comma => {
6216 self.advance();
6217 }
6218 Token::RParen => {
6219 self.advance();
6220 break;
6221 }
6222 other => {
6223 return Err(self.err(format!(
6224 "expected ',' or ')' in WITH (…) clause, got {other:?}"
6225 )));
6226 }
6227 }
6228 }
6229 }
6230 let partial_predicate = if matches!(self.peek(), Token::Where) {
6232 self.advance();
6233 Some(self.parse_expr(0)?)
6234 } else {
6235 None
6236 };
6237 if is_unique && !matches!(method, IndexMethod::BTree) {
6242 return Err(self.err(alloc::format!(
6243 "UNIQUE is only supported on BTree indexes, got USING {:?}",
6244 method
6245 )));
6246 }
6247 Ok(Statement::CreateIndex(CreateIndexStatement {
6248 name,
6249 table,
6250 column,
6251 method,
6252 if_not_exists,
6253 included_columns,
6254 partial_predicate,
6255 extra_columns: extra_columns.clone(),
6256 expression,
6257 is_unique,
6258 opclass,
6259 }))
6260 }
6261
6262 fn parse_column_def_with_fk(
6267 &mut self,
6268 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
6269 let col = self.parse_column_def()?;
6270 let inline_references = matches!(
6272 self.peek(),
6273 Token::Ident(s) if s.eq_ignore_ascii_case("references")
6274 );
6275 if !inline_references {
6276 return Ok((col, None));
6277 }
6278 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
6279 let fk = ForeignKeyConstraint {
6280 name: None,
6281 columns: vec![col.name.clone()],
6282 parent_table,
6283 parent_columns,
6284 on_delete,
6285 on_update,
6286 };
6287 Ok((col, Some(fk)))
6288 }
6289
6290 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
6298 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
6299 Ok(ty)
6300 }
6301
6302 #[allow(clippy::type_complexity)]
6303 fn parse_type_with_implied_flags(
6304 &mut self,
6305 ) -> Result<
6306 (
6307 ColumnTypeName,
6308 bool,
6309 bool,
6310 Option<String>,
6311 Collation,
6312 bool,
6313 Option<Vec<String>>,
6317 Option<Vec<String>>,
6320 ),
6321 ParseError,
6322 > {
6323 let mut ty_ident = match self.advance() {
6324 Token::Ident(s) => s,
6325 Token::Interval => "interval".to_string(),
6331 other => {
6332 return Err(ParseError {
6333 message: format!("expected column type, got {other:?}"),
6334 token_pos: self.pos.saturating_sub(1),
6335 });
6336 }
6337 };
6338 while matches!(self.peek(), Token::Dot) {
6343 self.advance();
6344 ty_ident = self.expect_ident_like()?;
6345 }
6346 let mut implied_auto_increment = false;
6347 let mut implied_not_null = false;
6348 let mut user_type_ref: Option<String> = None;
6349 let mut inline_enum_variants: Option<Vec<String>> = None;
6354 let mut inline_set_variants: Option<Vec<String>> = None;
6356 let mut ty = match ty_ident.as_str() {
6357 "smallserial" | "serial2" => {
6359 implied_auto_increment = true;
6360 implied_not_null = true;
6361 ColumnTypeName::SmallInt
6362 }
6363 "serial" | "serial4" => {
6364 implied_auto_increment = true;
6365 implied_not_null = true;
6366 ColumnTypeName::Int
6367 }
6368 "bigserial" | "serial8" => {
6369 implied_auto_increment = true;
6370 implied_not_null = true;
6371 ColumnTypeName::BigInt
6372 }
6373 "smallint" => {
6379 self.consume_optional_paren_size();
6384 ColumnTypeName::SmallInt
6385 }
6386 "tinyint" => {
6397 let width = self.peek_optional_paren_size_value();
6398 self.consume_optional_paren_size();
6399 if width == Some(1) {
6400 ColumnTypeName::Bool
6401 } else {
6402 ColumnTypeName::SmallInt
6403 }
6404 }
6405 "int" | "integer" | "mediumint" => {
6406 self.consume_optional_paren_size();
6407 ColumnTypeName::Int
6408 }
6409 "bigint" => {
6410 self.consume_optional_paren_size();
6411 ColumnTypeName::BigInt
6412 }
6413 "float" | "double" | "real" => {
6418 if ty_ident.eq_ignore_ascii_case("double")
6419 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6420 {
6421 self.advance();
6422 }
6423 ColumnTypeName::Float
6424 }
6425 "float4" | "float8" => ColumnTypeName::Float,
6427 "text" => ColumnTypeName::Text,
6428 "bool" | "boolean" => ColumnTypeName::Bool,
6429 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6430 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6431 "vector" => {
6432 let dim = self.parse_paren_size("VECTOR")?;
6433 let encoding = self.parse_optional_vector_encoding()?;
6434 ColumnTypeName::Vector { dim, encoding }
6435 }
6436 "numeric" => {
6437 let (precision, scale) = self.parse_optional_numeric_params()?;
6438 ColumnTypeName::Numeric(precision, scale)
6439 }
6440 "date" => ColumnTypeName::Date,
6441 "timestamp" | "datetime" => {
6444 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6450 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6451 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6452 {
6453 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6457 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6458 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6459 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6460 {
6461 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6465 } else {
6466 self.consume_optional_paren_size();
6470 ColumnTypeName::Timestamp
6471 }
6472 }
6473 "timestamptz" => ColumnTypeName::Timestamptz,
6477 "json" => ColumnTypeName::Json,
6482 "jsonb" => ColumnTypeName::Jsonb,
6483 "bytea" | "bytes" => ColumnTypeName::Bytes,
6489 "tsvector" => ColumnTypeName::TsVector,
6499 "tsquery" => ColumnTypeName::TsQuery,
6500 "uuid" => ColumnTypeName::Uuid,
6504 "interval" => ColumnTypeName::Interval,
6509 "time" => ColumnTypeName::Time,
6512 "year" => ColumnTypeName::Year,
6515 "timetz" => ColumnTypeName::TimeTz,
6518 "money" => ColumnTypeName::Money,
6521 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6523 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6524 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6525 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6526 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6527 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6528 "int4multirange" => ColumnTypeName::Multirange(RangeKindAst::Int4),
6530 "int8multirange" => ColumnTypeName::Multirange(RangeKindAst::Int8),
6531 "nummultirange" => ColumnTypeName::Multirange(RangeKindAst::Num),
6532 "tsmultirange" => ColumnTypeName::Multirange(RangeKindAst::Ts),
6533 "tstzmultirange" => ColumnTypeName::Multirange(RangeKindAst::TsTz),
6534 "datemultirange" => ColumnTypeName::Multirange(RangeKindAst::Date),
6535 "point" => ColumnTypeName::Point,
6537 "lseg" => ColumnTypeName::Lseg,
6538 "path" => ColumnTypeName::Path,
6539 "box" => ColumnTypeName::PgBox,
6540 "polygon" => ColumnTypeName::Polygon,
6541 "line" => ColumnTypeName::Line,
6542 "circle" => ColumnTypeName::Circle,
6543 "inet" => ColumnTypeName::Inet,
6545 "cidr" => ColumnTypeName::Cidr,
6546 "macaddr" => ColumnTypeName::Macaddr,
6547 "macaddr8" => ColumnTypeName::Macaddr8,
6548 "bit" => ColumnTypeName::Bit,
6549 "varbit" => ColumnTypeName::BitVarying,
6550 "xml" => ColumnTypeName::Xml,
6551 "hstore" => ColumnTypeName::Hstore,
6553 "enum" => {
6559 if !matches!(self.peek(), Token::LParen) {
6561 return Err(self.err(alloc::format!(
6562 "expected '(' after ENUM, got {:?}",
6563 self.peek()
6564 )));
6565 }
6566 self.advance();
6567 let mut variants: Vec<String> = Vec::new();
6568 loop {
6569 match self.advance() {
6570 Token::String(s) => variants.push(s),
6571 other => {
6572 return Err(self.err(alloc::format!(
6573 "ENUM(...) expects string literal variants, got {other:?}"
6574 )));
6575 }
6576 }
6577 match self.peek() {
6578 Token::Comma => {
6579 self.advance();
6580 continue;
6581 }
6582 Token::RParen => {
6583 self.advance();
6584 break;
6585 }
6586 other => {
6587 return Err(self.err(alloc::format!(
6588 "expected ',' or ')' in ENUM(...), got {other:?}"
6589 )));
6590 }
6591 }
6592 }
6593 if variants.is_empty() {
6594 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6595 }
6596 inline_enum_variants = Some(variants);
6597 ColumnTypeName::Text
6600 }
6601 "set" => {
6605 if !matches!(self.peek(), Token::LParen) {
6606 return Err(self.err(alloc::format!(
6607 "expected '(' after SET, got {:?}",
6608 self.peek()
6609 )));
6610 }
6611 self.advance();
6612 let mut variants: Vec<String> = Vec::new();
6613 loop {
6614 match self.advance() {
6615 Token::String(s) => variants.push(s),
6616 other => {
6617 return Err(self.err(alloc::format!(
6618 "SET(...) expects string literal variants, got {other:?}"
6619 )));
6620 }
6621 }
6622 match self.peek() {
6623 Token::Comma => {
6624 self.advance();
6625 continue;
6626 }
6627 Token::RParen => {
6628 self.advance();
6629 break;
6630 }
6631 other => {
6632 return Err(self.err(alloc::format!(
6633 "expected ',' or ')' in SET(...), got {other:?}"
6634 )));
6635 }
6636 }
6637 }
6638 if variants.is_empty() {
6639 return Err(self.err("SET(...) must declare at least one variant".into()));
6640 }
6641 inline_set_variants = Some(variants);
6642 ColumnTypeName::Text
6643 }
6644 _other => {
6645 user_type_ref = Some(ty_ident.clone());
6651 ColumnTypeName::Text
6652 }
6653 };
6654 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6664 {
6665 self.advance();
6666 true
6667 } else {
6668 false
6669 };
6670 let mut collation = Collation::Binary;
6686 loop {
6687 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6688 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6689 {
6690 self.advance(); self.advance(); if matches!(
6693 self.peek(),
6694 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6695 ) {
6696 self.advance();
6697 }
6698 continue;
6699 }
6700 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6701 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6708 match this.peek().clone() {
6709 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6710 this.advance();
6711 Some(s)
6712 }
6713 Token::Default => {
6714 this.advance();
6715 Some(alloc::string::String::from("default"))
6716 }
6717 _ => None,
6718 }
6719 };
6720 let raw = if let Some(head) = read_collation_atom(self) {
6721 if matches!(self.peek(), Token::Dot) {
6723 self.advance();
6724 let tail = read_collation_atom(self).unwrap_or_default();
6725 alloc::format!("{head}.{tail}")
6726 } else {
6727 head
6728 }
6729 } else {
6730 alloc::string::String::new()
6731 };
6732 if !raw.is_empty() {
6733 let parsed = Collation::from_collation_name(&raw);
6734 if parsed != Collation::Binary {
6740 collation = parsed;
6741 }
6742 }
6743 continue;
6744 }
6745 break;
6746 }
6747 if matches!(self.peek(), Token::LBracket) {
6752 self.advance();
6753 if !matches!(self.peek(), Token::RBracket) {
6754 return Err(self.err(alloc::format!(
6755 "TEXT[] takes no dimension; got {:?}",
6756 self.peek()
6757 )));
6758 }
6759 self.advance();
6760 ty = match ty {
6764 ColumnTypeName::Text => ColumnTypeName::TextArray,
6765 ColumnTypeName::Int => ColumnTypeName::IntArray,
6766 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6767 ColumnTypeName::Interval => ColumnTypeName::IntervalArray,
6770 ColumnTypeName::Bool => ColumnTypeName::BoolArray,
6772 ColumnTypeName::SmallInt => ColumnTypeName::SmallIntArray,
6773 ColumnTypeName::Float => ColumnTypeName::FloatArray,
6774 ColumnTypeName::Numeric(_, _) => ColumnTypeName::NumericArray,
6778 ColumnTypeName::Date => ColumnTypeName::DateArray,
6779 ColumnTypeName::Timestamp => ColumnTypeName::TimestampArray,
6780 ColumnTypeName::Timestamptz => ColumnTypeName::TimestamptzArray,
6781 ColumnTypeName::Uuid => ColumnTypeName::UuidArray,
6782 ColumnTypeName::Json => ColumnTypeName::JsonArray,
6783 ColumnTypeName::Jsonb => ColumnTypeName::JsonbArray,
6784 ColumnTypeName::Bytes => ColumnTypeName::BytesArray,
6785 ColumnTypeName::Varchar(_) => ColumnTypeName::VarcharArray,
6789 ColumnTypeName::Char(_) => ColumnTypeName::CharArray,
6790 ColumnTypeName::Money => ColumnTypeName::MoneyArray,
6793 other => {
6794 return Err(self.err(alloc::format!("{other:?}[] not yet supported")));
6795 }
6796 };
6797 if matches!(self.peek(), Token::LBracket) {
6800 self.advance();
6801 if !matches!(self.peek(), Token::RBracket) {
6802 return Err(self.err(alloc::format!(
6803 "TYPE[][] second dimension takes no size; got {:?}",
6804 self.peek()
6805 )));
6806 }
6807 self.advance();
6808 ty = match ty {
6809 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6810 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6811 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6812 other => {
6813 return Err(self.err(alloc::format!(
6814 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6815 TEXT[][] only; got {other:?}"
6816 )));
6817 }
6818 };
6819 }
6820 }
6821 Ok((
6822 ty,
6823 implied_auto_increment,
6824 implied_not_null,
6825 user_type_ref,
6826 collation,
6827 is_unsigned,
6828 inline_enum_variants,
6829 inline_set_variants,
6830 ))
6831 }
6832
6833 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6834 if let Token::Ident(s) = self.peek()
6843 && [
6844 "unique",
6845 "primary",
6846 "foreign",
6847 "constraint",
6848 "check",
6849 "references",
6850 "exclude",
6851 ]
6852 .iter()
6853 .any(|kw| s.eq_ignore_ascii_case(kw))
6854 {
6855 return Err(self.err(alloc::format!(
6856 "unexpected reserved keyword '{s}' at start of column definition \
6857 (malformed table constraint?)"
6858 )));
6859 }
6860 let name = self.expect_ident_like()?;
6861 let (
6862 ty,
6863 implied_auto_increment,
6864 implied_not_null,
6865 user_type_ref,
6866 collation,
6867 is_unsigned,
6868 inline_enum_variants,
6869 inline_set_variants,
6870 ) = self.parse_type_with_implied_flags()?;
6871 let mut default: Option<Expr> = None;
6875 let mut nullable = !implied_not_null;
6876 let mut nullability_seen = implied_not_null;
6877 let mut auto_increment = implied_auto_increment;
6878 let mut is_primary_key = false;
6879 let mut is_unique = false;
6880 let mut check: Option<Expr> = None;
6881 let mut on_update_runtime: Option<Expr> = None;
6882 let mut generated_stored_expr: Option<Box<Expr>> = None;
6883 loop {
6884 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
6890 self.advance();
6891 let _name = self.expect_ident_like()?;
6892 continue;
6893 }
6894 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
6904 self.advance();
6905 match self.peek().clone() {
6906 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
6907 self.advance();
6908 }
6909 Token::By => {
6911 self.advance();
6912 if !matches!(self.peek(), Token::Default) {
6913 return Err(self.err(alloc::format!(
6914 "expected DEFAULT after GENERATED BY, got {:?}",
6915 self.peek()
6916 )));
6917 }
6918 self.advance();
6919 }
6920 other => {
6921 return Err(self.err(alloc::format!(
6922 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
6923 )));
6924 }
6925 }
6926 if !matches!(self.peek(), Token::As) {
6927 return Err(self.err(alloc::format!(
6928 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
6929 self.peek()
6930 )));
6931 }
6932 self.advance();
6933 if matches!(self.peek(), Token::LParen) {
6940 self.advance();
6941 let expr = self.parse_expr(0)?;
6942 if !matches!(self.peek(), Token::RParen) {
6943 return Err(self.err(alloc::format!(
6944 "expected ')' after GENERATED ALWAYS AS (<expr>), got {:?}",
6945 self.peek()
6946 )));
6947 }
6948 self.advance();
6949 let stored = match self.peek() {
6950 Token::Ident(s) | Token::QuotedIdent(s)
6951 if s.eq_ignore_ascii_case("stored") =>
6952 {
6953 self.advance();
6954 true
6955 }
6956 Token::Ident(s) | Token::QuotedIdent(s)
6957 if s.eq_ignore_ascii_case("virtual") =>
6958 {
6959 return Err(self.err(
6960 "GENERATED ALWAYS AS (expr) VIRTUAL is not supported \
6961 at v7.37.7; use STORED"
6962 .into(),
6963 ));
6964 }
6965 other => {
6966 return Err(self.err(alloc::format!(
6967 "expected STORED after GENERATED ALWAYS AS (<expr>), \
6968 got {other:?}"
6969 )));
6970 }
6971 };
6972 let _ = stored; generated_stored_expr = Some(Box::new(expr));
6974 continue;
6975 }
6976 self.expect_keyword_ident("identity")?;
6977 if matches!(self.peek(), Token::LParen) {
6981 let mut depth = 0usize;
6982 loop {
6983 match self.advance() {
6984 Token::LParen => depth += 1,
6985 Token::RParen => {
6986 depth -= 1;
6987 if depth == 0 {
6988 break;
6989 }
6990 }
6991 Token::Eof => {
6992 return Err(self.err(
6993 "unterminated sequence-options parens after IDENTITY".into(),
6994 ));
6995 }
6996 _ => {}
6997 }
6998 }
6999 }
7000 auto_increment = true;
7001 nullable = false;
7003 continue;
7004 }
7005 if matches!(self.peek(), Token::On)
7010 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
7011 {
7012 self.advance(); self.advance(); let next = self.peek().clone();
7016 match next {
7017 Token::Ident(s) | Token::QuotedIdent(s)
7018 if s.eq_ignore_ascii_case("current_timestamp") =>
7019 {
7020 self.advance();
7021 if matches!(self.peek(), Token::LParen) {
7023 self.advance();
7024 if !matches!(self.peek(), Token::Integer(_)) {
7025 return Err(self.err(alloc::format!(
7026 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
7027 self.peek()
7028 )));
7029 }
7030 self.advance();
7031 if !matches!(self.peek(), Token::RParen) {
7032 return Err(self.err(alloc::format!(
7033 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
7034 self.peek()
7035 )));
7036 }
7037 self.advance();
7038 }
7039 on_update_runtime = Some(Expr::FunctionCall {
7040 name: "now".into(),
7041 args: Vec::new(),
7042 });
7043 continue;
7044 }
7045 other => {
7046 return Err(self.err(alloc::format!(
7047 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
7048 )));
7049 }
7050 }
7051 }
7052 if matches!(self.peek(), Token::Default) {
7053 if default.is_some() {
7054 return Err(self.err("DEFAULT specified twice".into()));
7055 }
7056 self.advance();
7057 default = Some(self.parse_expr(0)?);
7058 continue;
7059 }
7060 if matches!(self.peek(), Token::Not) {
7061 if nullability_seen {
7062 return Err(self.err("NOT NULL specified twice".into()));
7063 }
7064 self.advance();
7065 if !matches!(self.peek(), Token::Null) {
7066 return Err(self.err(format!(
7067 "expected NULL after NOT in column def, got {:?}",
7068 self.peek()
7069 )));
7070 }
7071 self.advance();
7072 nullable = false;
7073 nullability_seen = true;
7074 continue;
7075 }
7076 if matches!(self.peek(), Token::Null) {
7082 if nullability_seen && !nullable {
7083 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
7084 }
7085 self.advance();
7086 nullable = true;
7087 nullability_seen = true;
7088 continue;
7089 }
7090 if let Token::Ident(s) = self.peek()
7093 && (s.eq_ignore_ascii_case("auto_increment")
7094 || s.eq_ignore_ascii_case("autoincrement"))
7095 {
7096 if auto_increment {
7097 return Err(self.err("AUTO_INCREMENT specified twice".into()));
7098 }
7099 self.advance();
7100 auto_increment = true;
7101 continue;
7102 }
7103 if let Token::Ident(s) = self.peek()
7108 && s.eq_ignore_ascii_case("primary")
7109 {
7110 if is_primary_key {
7111 return Err(self.err("PRIMARY KEY specified twice".into()));
7112 }
7113 let next = self.tokens.get(self.pos + 1);
7115 let next_is_key = matches!(
7116 next,
7117 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
7118 );
7119 if !next_is_key {
7120 return Err(self.err(format!(
7121 "expected KEY after PRIMARY in column def, got {:?}",
7122 next
7123 )));
7124 }
7125 self.advance(); self.advance(); is_primary_key = true;
7128 if nullability_seen && nullable {
7129 return Err(self.err(
7130 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
7131 ));
7132 }
7133 nullable = false;
7134 nullability_seen = true;
7135 continue;
7136 }
7137 if let Token::Ident(s) = self.peek()
7141 && s.eq_ignore_ascii_case("unique")
7142 {
7143 if is_unique {
7144 return Err(self.err("UNIQUE specified twice".into()));
7145 }
7146 self.advance();
7147 is_unique = true;
7148 continue;
7149 }
7150 if let Token::Ident(s) = self.peek()
7155 && s.eq_ignore_ascii_case("check")
7156 {
7157 self.advance();
7158 if !matches!(self.peek(), Token::LParen) {
7159 return Err(self.err(alloc::format!(
7160 "expected '(' after CHECK in column def, got {:?}",
7161 self.peek()
7162 )));
7163 }
7164 self.advance();
7165 let pred = self.parse_expr(0)?;
7166 if !matches!(self.peek(), Token::RParen) {
7167 return Err(self.err(alloc::format!(
7168 "expected ')' to close CHECK predicate, got {:?}",
7169 self.peek()
7170 )));
7171 }
7172 self.advance();
7173 check = Some(match check.take() {
7174 Some(prev) => Expr::Binary {
7175 op: BinOp::And,
7176 lhs: Box::new(prev),
7177 rhs: Box::new(pred),
7178 },
7179 None => pred,
7180 });
7181 continue;
7182 }
7183 break;
7184 }
7185 Ok(ColumnDef {
7186 name,
7187 ty,
7188 nullable,
7189 default,
7190 auto_increment,
7191 is_primary_key,
7192 is_unique,
7193 check,
7194 user_type_ref,
7195 on_update_runtime,
7196 collation,
7197 is_unsigned,
7198 inline_enum_variants,
7199 inline_set_variants,
7200 generated_stored_expr,
7201 })
7202 }
7203
7204 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
7208 if !matches!(self.peek(), Token::LParen) {
7209 return Ok((0, 0));
7213 }
7214 self.advance();
7215 let precision = match self.advance() {
7216 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
7217 other => {
7218 return Err(ParseError {
7219 message: format!(
7220 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
7221 ),
7222 token_pos: self.pos.saturating_sub(1),
7223 });
7224 }
7225 };
7226 let scale = if matches!(self.peek(), Token::Comma) {
7227 self.advance();
7228 match self.advance() {
7229 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
7230 u8::try_from(n).expect("range-checked")
7231 }
7232 other => {
7233 return Err(ParseError {
7234 message: format!(
7235 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
7236 ),
7237 token_pos: self.pos.saturating_sub(1),
7238 });
7239 }
7240 }
7241 } else {
7242 0
7243 };
7244 if !matches!(self.peek(), Token::RParen) {
7245 return Err(self.err(format!(
7246 "expected ')' to close NUMERIC params, got {:?}",
7247 self.peek()
7248 )));
7249 }
7250 self.advance();
7251 Ok((precision, scale))
7252 }
7253
7254 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
7262 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
7263 return Ok(VecEncoding::F32);
7264 }
7265 let n1 = self.tokens.get(self.pos + 1);
7271 let next_is_encoding = matches!(
7272 n1,
7273 Some(Token::Ident(s))
7274 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
7275 );
7276 if !next_is_encoding {
7277 return Ok(VecEncoding::F32);
7278 }
7279 self.advance();
7280 let enc_ident = match self.advance() {
7281 Token::Ident(s) => s,
7282 other => {
7283 return Err(self.err(format!(
7284 "expected vector encoding after USING, got {other:?}"
7285 )));
7286 }
7287 };
7288 match enc_ident.to_ascii_lowercase().as_str() {
7289 "sq8" => Ok(VecEncoding::Sq8),
7290 "half" => Ok(VecEncoding::F16),
7293 other => Err(self.err(format!(
7294 "unknown vector encoding {other:?}; supported: SQ8, HALF"
7295 ))),
7296 }
7297 }
7298
7299 fn peek_optional_paren_size_value(&self) -> Option<i64> {
7305 if !matches!(self.peek(), Token::LParen) {
7306 return None;
7307 }
7308 let next = self.tokens.get(self.pos + 1)?;
7309 let n = match next {
7310 Token::Integer(n) => *n,
7311 _ => return None,
7312 };
7313 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
7314 return None;
7315 }
7316 Some(n)
7317 }
7318
7319 fn consume_optional_paren_size(&mut self) {
7323 if !matches!(self.peek(), Token::LParen) {
7324 return;
7325 }
7326 self.advance();
7327 let mut depth = 1usize;
7329 while depth > 0 {
7330 match self.peek() {
7331 Token::LParen => depth += 1,
7332 Token::RParen => depth -= 1,
7333 Token::Eof => return,
7334 _ => {}
7335 }
7336 self.advance();
7337 }
7338 }
7339
7340 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
7341 if !matches!(self.peek(), Token::LParen) {
7342 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
7343 }
7344 self.advance();
7345 let n = match self.advance() {
7346 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
7347 message: format!("{label} size too large: {n}"),
7348 token_pos: self.pos.saturating_sub(1),
7349 })?,
7350 other => {
7351 return Err(ParseError {
7352 message: format!("expected positive integer {label} size, got {other:?}"),
7353 token_pos: self.pos.saturating_sub(1),
7354 });
7355 }
7356 };
7357 if !matches!(self.peek(), Token::RParen) {
7358 return Err(self.err(format!(
7359 "expected ')' after {label} size, got {:?}",
7360 self.peek()
7361 )));
7362 }
7363 self.advance();
7364 Ok(n)
7365 }
7366
7367 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
7368 debug_assert!(matches!(self.peek(), Token::Insert));
7369 self.advance();
7370 if !matches!(self.peek(), Token::Into) {
7371 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
7372 }
7373 self.advance();
7374 let table = self.expect_ident_like()?;
7375 let columns = if matches!(self.peek(), Token::LParen) {
7377 self.advance();
7378 let mut names = Vec::new();
7379 loop {
7380 names.push(self.expect_ident_like()?);
7381 match self.peek() {
7382 Token::Comma => {
7383 self.advance();
7384 }
7385 Token::RParen => {
7386 self.advance();
7387 break;
7388 }
7389 other => {
7390 return Err(self.err(format!(
7391 "expected ',' or ')' in INSERT column list, got {other:?}"
7392 )));
7393 }
7394 }
7395 }
7396 Some(names)
7397 } else {
7398 None
7399 };
7400 if matches!(self.peek(), Token::Select) {
7403 let select_stmt = match self.parse_select_stmt()? {
7404 Statement::Select(s) => s,
7405 other => {
7406 return Err(self.err(alloc::format!(
7407 "expected SELECT after INSERT INTO ... target, got {other:?}"
7408 )));
7409 }
7410 };
7411 let on_conflict = self.parse_optional_on_conflict()?;
7412 let returning = self.parse_optional_returning()?;
7413 return Ok(Statement::Insert(InsertStatement {
7414 ctes: Vec::new(),
7415 table,
7416 columns,
7417 rows: Vec::new(),
7418 select_source: Some(Box::new(select_stmt)),
7419 on_conflict,
7420 returning,
7421 }));
7422 }
7423 if !matches!(self.peek(), Token::Values) {
7424 return Err(self.err(format!(
7425 "expected VALUES or SELECT after table name, got {:?}",
7426 self.peek()
7427 )));
7428 }
7429 self.advance();
7430 if !matches!(self.peek(), Token::LParen) {
7431 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
7432 }
7433 let mut rows = Vec::new();
7434 loop {
7435 if !matches!(self.peek(), Token::LParen) {
7437 return Err(self.err(format!(
7438 "expected '(' for next VALUES tuple, got {:?}",
7439 self.peek()
7440 )));
7441 }
7442 self.advance();
7443 let mut tuple = Vec::new();
7444 loop {
7445 tuple.push(self.parse_expr(0)?);
7446 match self.peek() {
7447 Token::Comma => {
7448 self.advance();
7449 }
7450 Token::RParen => {
7451 self.advance();
7452 break;
7453 }
7454 other => {
7455 return Err(self.err(format!(
7456 "expected ',' or ')' in VALUES tuple, got {other:?}"
7457 )));
7458 }
7459 }
7460 }
7461 if tuple.is_empty() {
7462 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
7463 }
7464 rows.push(tuple);
7465 if matches!(self.peek(), Token::Comma) {
7467 self.advance();
7468 } else {
7469 break;
7470 }
7471 }
7472 let on_conflict = self.parse_optional_on_conflict()?;
7473 let returning = self.parse_optional_returning()?;
7474 Ok(Statement::Insert(InsertStatement {
7475 ctes: Vec::new(),
7476 table,
7477 columns,
7478 rows,
7479 select_source: None,
7480 on_conflict,
7481 returning,
7482 }))
7483 }
7484
7485 fn parse_optional_on_conflict(
7490 &mut self,
7491 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7492 if !matches!(self.peek(), Token::On) {
7493 return Ok(None);
7494 }
7495 let next_is_conflict = matches!(
7498 self.tokens.get(self.pos + 1),
7499 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7500 );
7501 if !next_is_conflict {
7502 return Ok(None);
7503 }
7504 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7508 if matches!(self.peek(), Token::LParen) {
7509 self.advance();
7510 loop {
7511 target_columns.push(self.expect_ident_like()?);
7512 match self.peek() {
7513 Token::Comma => {
7514 self.advance();
7515 }
7516 Token::RParen => {
7517 self.advance();
7518 break;
7519 }
7520 other => {
7521 return Err(self.err(alloc::format!(
7522 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7523 )));
7524 }
7525 }
7526 }
7527 }
7528 match self.advance() {
7530 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7531 other => {
7532 return Err(self.err(alloc::format!(
7533 "expected DO after ON CONFLICT [(…)], got {other:?}"
7534 )));
7535 }
7536 }
7537 let action = match self.advance() {
7539 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7540 crate::ast::OnConflictAction::Nothing
7541 }
7542 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7543 self.parse_on_conflict_update_action()?
7544 }
7545 other => {
7546 return Err(self.err(alloc::format!(
7547 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7548 )));
7549 }
7550 };
7551 Ok(Some(crate::ast::OnConflictClause {
7552 target_columns,
7553 action,
7554 }))
7555 }
7556
7557 fn parse_on_conflict_update_action(
7561 &mut self,
7562 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7563 match self.advance() {
7565 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7566 other => {
7567 return Err(self.err(alloc::format!(
7568 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7569 )));
7570 }
7571 }
7572 let mut assignments: Vec<(String, Expr)> = Vec::new();
7573 loop {
7574 let col = self.expect_ident_like()?;
7575 if !matches!(self.peek(), Token::Eq) {
7576 return Err(self.err(alloc::format!(
7577 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7578 self.peek()
7579 )));
7580 }
7581 self.advance();
7582 let value = self.parse_expr(0)?;
7583 assignments.push((col, value));
7584 if matches!(self.peek(), Token::Comma) {
7585 self.advance();
7586 continue;
7587 }
7588 break;
7589 }
7590 let where_ = if matches!(self.peek(), Token::Where) {
7591 self.advance();
7592 Some(self.parse_expr(0)?)
7593 } else {
7594 None
7595 };
7596 Ok(crate::ast::OnConflictAction::Update {
7597 assignments,
7598 where_,
7599 })
7600 }
7601
7602 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7603 let mut items = Vec::new();
7604 loop {
7605 items.push(self.parse_select_item()?);
7606 if matches!(self.peek(), Token::Comma) {
7607 self.advance();
7608 } else {
7609 break;
7610 }
7611 }
7612 Ok(items)
7613 }
7614
7615 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7616 if matches!(self.peek(), Token::Star) {
7617 self.advance();
7618 return Ok(SelectItem::Wildcard);
7619 }
7620 let expr = self.parse_expr(0)?;
7621 let alias = self.parse_optional_alias();
7622 Ok(SelectItem::Expr { expr, alias })
7623 }
7624
7625 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7626 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7636 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("jsonb_each_text"))
7637 && matches!(self.tokens.get(self.pos + 2), Some(Token::LParen))
7638 {
7639 self.advance(); self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7643 if !matches!(self.peek(), Token::RParen) {
7644 return Err(self.err(alloc::format!(
7645 "expected ')' after LATERAL jsonb_each_text() argument, got {:?}",
7646 self.peek()
7647 )));
7648 }
7649 self.advance();
7650 let (alias_ident, column_aliases) = self.parse_optional_alias_with_columns();
7651 let alias = alias_ident
7652 .clone()
7653 .unwrap_or_else(|| "jsonb_each_text".to_string());
7654 let srf_alias = "__srf__".to_string();
7660 let key_alias = column_aliases
7661 .first()
7662 .cloned()
7663 .unwrap_or_else(|| "key".to_string());
7664 let value_alias = column_aliases
7665 .get(1)
7666 .cloned()
7667 .unwrap_or_else(|| "value".to_string());
7668 let inner_select = crate::ast::SelectStatement {
7669 ctes: Vec::new(),
7670 distinct: false,
7671 items: alloc::vec![
7672 crate::ast::SelectItem::Expr {
7673 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7674 qualifier: Some(srf_alias.clone()),
7675 name: "key".to_string(),
7676 }),
7677 alias: Some(key_alias),
7678 },
7679 crate::ast::SelectItem::Expr {
7680 expr: crate::ast::Expr::Column(crate::ast::ColumnName {
7681 qualifier: Some(srf_alias.clone()),
7682 name: "value".to_string(),
7683 }),
7684 alias: Some(value_alias),
7685 },
7686 ],
7687 from: Some(crate::ast::FromClause {
7688 primary: TableRef {
7689 name: srf_alias.clone(),
7690 alias: Some(srf_alias.clone()),
7691 as_of_segment: None,
7692 unnest_expr: None,
7693 unnest_column_aliases: Vec::new(),
7694 generate_series_args: None,
7695 lateral_subquery: None,
7696 jsonb_each_text_arg: Some(Box::new(arg)),
7697 },
7698 joins: Vec::new(),
7699 }),
7700 where_: None,
7701 group_by: None,
7702 group_by_all: false,
7703 having: None,
7704 unions: Vec::new(),
7705 order_by: Vec::new(),
7706 limit: None,
7707 offset: None,
7708 limit_with_ties: false,
7709 };
7710 return Ok(TableRef {
7711 name: alias.clone(),
7712 alias: Some(alias),
7713 as_of_segment: None,
7714 unnest_expr: None,
7715 unnest_column_aliases: Vec::new(),
7716 generate_series_args: None,
7717 lateral_subquery: Some(Box::new(inner_select)),
7718 jsonb_each_text_arg: None,
7719 });
7720 }
7721 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7741 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7742 {
7743 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7747 Statement::Select(s) => s,
7748 other => {
7749 return Err(self.err(alloc::format!(
7750 "expected SELECT inside LATERAL ( … ), got {other:?}"
7751 )));
7752 }
7753 };
7754 if !matches!(self.peek(), Token::RParen) {
7755 return Err(self.err(alloc::format!(
7756 "expected ')' after LATERAL subquery, got {:?}",
7757 self.peek()
7758 )));
7759 }
7760 self.advance();
7761 let alias_ident = self.parse_optional_alias();
7762 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7763 return Ok(TableRef {
7764 name,
7765 alias: alias_ident,
7766 as_of_segment: None,
7767 unnest_expr: None,
7768 unnest_column_aliases: Vec::new(),
7769 generate_series_args: None,
7770 lateral_subquery: Some(Box::new(inner)),
7771 jsonb_each_text_arg: None,
7772 });
7773 }
7774 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("jsonb_each_text"))
7782 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7783 {
7784 self.advance(); self.advance(); let arg = self.parse_expr(0)?;
7787 if !matches!(self.peek(), Token::RParen) {
7788 return Err(self.err(alloc::format!(
7789 "expected ')' after jsonb_each_text() argument, got {:?}",
7790 self.peek()
7791 )));
7792 }
7793 self.advance();
7794 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7795 let name = alias_ident
7796 .clone()
7797 .unwrap_or_else(|| "jsonb_each_text".to_string());
7798 return Ok(TableRef {
7799 name,
7800 alias: alias_ident,
7801 as_of_segment: None,
7802 unnest_expr: None,
7803 unnest_column_aliases: Vec::new(),
7804 generate_series_args: None,
7805 lateral_subquery: None,
7806 jsonb_each_text_arg: Some(Box::new(arg)),
7807 });
7808 }
7809 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
7813 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7814 {
7815 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
7818 if !matches!(self.peek(), Token::RParen) {
7819 return Err(self.err(alloc::format!(
7820 "expected ')' after unnest() argument, got {:?}",
7821 self.peek()
7822 )));
7823 }
7824 self.advance();
7825 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
7826 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
7827 return Ok(TableRef {
7828 name,
7829 alias: alias_ident,
7830 as_of_segment: None,
7831 unnest_expr: Some(Box::new(expr)),
7832 unnest_column_aliases,
7833 generate_series_args: None,
7834 lateral_subquery: None,
7835 jsonb_each_text_arg: None,
7836 });
7837 }
7838 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
7848 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7849 {
7850 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
7853 loop {
7854 args.push(self.parse_expr(0)?);
7855 if matches!(self.peek(), Token::Comma) {
7856 self.advance();
7857 continue;
7858 }
7859 break;
7860 }
7861 if !matches!(self.peek(), Token::RParen) {
7862 return Err(self.err(alloc::format!(
7863 "expected ')' after generate_series() arguments, got {:?}",
7864 self.peek()
7865 )));
7866 }
7867 self.advance();
7868 if args.len() < 2 || args.len() > 3 {
7869 return Err(self.err(alloc::format!(
7870 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
7871 args.len()
7872 )));
7873 }
7874 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7875 let name = alias_ident
7876 .clone()
7877 .unwrap_or_else(|| "generate_series".to_string());
7878 return Ok(TableRef {
7879 name,
7880 alias: alias_ident,
7881 as_of_segment: None,
7882 unnest_expr: None,
7883 unnest_column_aliases: Vec::new(),
7884 generate_series_args: Some(args),
7885 lateral_subquery: None,
7886 jsonb_each_text_arg: None,
7887 });
7888 }
7889 let name = if let Some(synth) = self.try_peek_meta_qualified() {
7898 synth
7899 } else if let Some(synth) = self.try_peek_meta_bare() {
7900 synth
7901 } else {
7902 self.expect_ident_like()?
7903 };
7904 let as_of_segment = if matches!(self.peek(), Token::As)
7910 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
7911 {
7912 self.advance(); self.advance(); let kw = match self.peek().clone() {
7915 Token::Ident(s) | Token::QuotedIdent(s) => s,
7916 other => {
7917 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
7918 }
7919 };
7920 if !kw.eq_ignore_ascii_case("segment") {
7921 return Err(self.err(format!(
7922 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
7923 )));
7924 }
7925 self.advance();
7926 let id = match self.advance() {
7929 Token::String(s) => s
7930 .parse::<u32>()
7931 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7932 Token::Integer(n) => u32::try_from(n)
7933 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7934 other => {
7935 return Err(self.err(format!(
7936 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7937 )));
7938 }
7939 };
7940 Some(id)
7941 } else {
7942 None
7943 };
7944 let alias = self.parse_optional_alias();
7945 Ok(TableRef {
7946 name,
7947 alias,
7948 as_of_segment,
7949 unnest_expr: None,
7950 unnest_column_aliases: Vec::new(),
7951 generate_series_args: None,
7952 lateral_subquery: None,
7953 jsonb_each_text_arg: None,
7954 })
7955 }
7956
7957 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
7963 let alias = self.parse_optional_alias();
7964 if alias.is_none() {
7965 return (None, Vec::new());
7966 }
7967 let mut cols: Vec<String> = Vec::new();
7968 if matches!(self.peek(), Token::LParen) {
7969 self.advance();
7970 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7971 self.advance();
7972 cols.push(s);
7973 if matches!(self.peek(), Token::Comma) {
7974 self.advance();
7975 continue;
7976 }
7977 break;
7978 }
7979 if matches!(self.peek(), Token::RParen) {
7980 self.advance();
7981 }
7982 }
7983 (alias, cols)
7984 }
7985
7986 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7991 let primary = self.parse_table_ref()?;
7992 let mut joins = Vec::new();
7993 loop {
7994 if matches!(self.peek(), Token::Comma) {
7996 self.advance();
7997 let table = self.parse_table_ref()?;
7998 joins.push(FromJoin {
7999 kind: JoinKind::Cross,
8000 table,
8001 on: None,
8002 });
8003 continue;
8004 }
8005 let kind =
8008 match self.peek() {
8009 Token::Inner => {
8010 self.advance();
8011 if !matches!(self.peek(), Token::Join) {
8012 return Err(self
8013 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
8014 }
8015 self.advance();
8016 JoinKind::Inner
8017 }
8018 Token::Left => {
8019 self.advance();
8020 if matches!(self.peek(), Token::Outer) {
8021 self.advance();
8022 }
8023 if !matches!(self.peek(), Token::Join) {
8024 return Err(self.err(format!(
8025 "expected JOIN after LEFT [OUTER], got {:?}",
8026 self.peek()
8027 )));
8028 }
8029 self.advance();
8030 JoinKind::Left
8031 }
8032 Token::Cross => {
8033 self.advance();
8034 if !matches!(self.peek(), Token::Join) {
8035 return Err(self
8036 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
8037 }
8038 self.advance();
8039 JoinKind::Cross
8040 }
8041 Token::Join => {
8042 self.advance();
8043 JoinKind::Inner
8044 }
8045 _ => break,
8046 };
8047 let table = self.parse_table_ref()?;
8048 let on = if matches!(self.peek(), Token::On) {
8049 self.advance();
8050 Some(self.parse_expr(0)?)
8051 } else if kind == JoinKind::Cross {
8052 None
8053 } else {
8054 return Err(self.err(format!(
8055 "expected ON after {:?} JOIN, got {:?}",
8056 kind,
8057 self.peek()
8058 )));
8059 };
8060 joins.push(FromJoin { kind, table, on });
8061 }
8062 Ok(FromClause { primary, joins })
8063 }
8064
8065 fn parse_optional_alias(&mut self) -> Option<String> {
8070 if matches!(self.peek(), Token::As) {
8071 self.advance();
8072 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
8077 return self.expect_ident_like().ok();
8078 }
8079 return None;
8080 }
8081 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8089 if is_alias_stopword(s) {
8090 return None;
8091 }
8092 return self.expect_ident_like().ok();
8093 }
8094 None
8095 }
8096
8097 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8099 self.enter_nested()?;
8103 let r = self.parse_expr_inner(min_prec);
8104 self.nest_depth -= 1;
8105 r
8106 }
8107
8108 fn parse_expr_inner(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
8109 let mut lhs = self.parse_unary()?;
8110 let mut chain_len = 0usize;
8111 while let Some((op, prec)) = binop_from(self.peek()) {
8112 if prec < min_prec {
8113 break;
8114 }
8115 chain_len += 1;
8119 if chain_len > MAX_BINARY_CHAIN {
8120 return Err(self.err(alloc::format!(
8121 "more than {MAX_BINARY_CHAIN} chained binary operators; rewrite long OR-equality chains as IN (…)"
8122 )));
8123 }
8124 self.advance();
8125 let any_kind = match self.peek() {
8130 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
8131 Some(false)
8132 }
8133 Token::Ident(s) | Token::QuotedIdent(s)
8134 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
8135 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
8136 {
8137 Some(s.eq_ignore_ascii_case("any"))
8138 }
8139 _ => None,
8140 };
8141 if let Some(is_any) = any_kind {
8142 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
8145 if !matches!(self.peek(), Token::RParen) {
8146 return Err(self.err(alloc::format!(
8147 "expected ')' after ANY/ALL argument, got {:?}",
8148 self.peek()
8149 )));
8150 }
8151 self.advance();
8152 lhs = Expr::AnyAll {
8153 expr: Box::new(lhs),
8154 op,
8155 array: Box::new(arr),
8156 is_any,
8157 };
8158 continue;
8159 }
8160 let rhs = self.parse_expr(prec + 1)?;
8161 lhs = Expr::Binary {
8162 lhs: Box::new(lhs),
8163 op,
8164 rhs: Box::new(rhs),
8165 };
8166 }
8167 Ok(lhs)
8168 }
8169
8170 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
8171 match self.peek() {
8172 Token::Not => {
8173 self.advance();
8174 let e = self.parse_expr(3)?;
8177 Ok(Expr::Unary {
8178 op: UnOp::Not,
8179 expr: Box::new(e),
8180 })
8181 }
8182 Token::Minus => {
8183 self.advance();
8184 let e = self.parse_expr(8)?;
8187 Ok(Expr::Unary {
8188 op: UnOp::Neg,
8189 expr: Box::new(e),
8190 })
8191 }
8192 Token::Tilde => {
8193 self.advance();
8194 let e = self.parse_expr(8)?;
8196 Ok(Expr::Unary {
8197 op: UnOp::BitNot,
8198 expr: Box::new(e),
8199 })
8200 }
8201 _ => self.parse_atom(),
8202 }
8203 }
8204
8205 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
8206 let tok_pos = self.pos;
8207 match self.advance() {
8208 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
8209 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
8210 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
8211 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
8212 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
8213 Token::Null => Ok(Expr::Literal(Literal::Null)),
8214 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
8218 Token::LParen => {
8219 if matches!(self.peek(), Token::Select) {
8223 let inner = self.parse_select_stmt()?;
8224 match self.advance() {
8225 Token::RParen => {
8226 let Statement::Select(s) = inner else {
8227 unreachable!("parse_select_stmt returns Select")
8228 };
8229 Ok(Expr::ScalarSubquery(Box::new(s)))
8230 }
8231 other => Err(ParseError {
8232 message: format!("expected ')' after scalar subquery, got {other:?}"),
8233 token_pos: self.pos.saturating_sub(1),
8234 }),
8235 }
8236 } else {
8237 let e = self.parse_expr(0)?;
8238 match self.advance() {
8239 Token::RParen => Ok(e),
8240 other => Err(ParseError {
8241 message: format!("expected ')', got {other:?}"),
8242 token_pos: self.pos.saturating_sub(1),
8243 }),
8244 }
8245 }
8246 }
8247 Token::LBracket => self.parse_vector_literal_body(),
8248 Token::Extract => self.parse_extract_atom(),
8249 Token::Interval => self.parse_interval_atom(),
8250 Token::Left if matches!(self.peek(), Token::LParen) => {
8257 self.advance(); let mut args = Vec::new();
8259 if !matches!(self.peek(), Token::RParen) {
8260 loop {
8261 args.push(self.parse_expr(0)?);
8262 match self.peek() {
8263 Token::Comma => {
8264 self.advance();
8265 }
8266 Token::RParen => break,
8267 other => {
8268 return Err(self.err(alloc::format!(
8269 "expected ',' or ')' in left() args, got {other:?}"
8270 )));
8271 }
8272 }
8273 }
8274 }
8275 self.advance(); Ok(Expr::FunctionCall {
8277 name: "left".into(),
8278 args,
8279 })
8280 }
8281 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
8286 self.parse_exists_atom(false)
8287 }
8288 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
8292 self.parse_case_atom()
8293 }
8294 Token::Ident(s) | Token::QuotedIdent(s)
8298 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
8299 {
8300 self.advance(); let mut items: Vec<Expr> = Vec::new();
8302 if !matches!(self.peek(), Token::RBracket) {
8303 loop {
8304 items.push(self.parse_expr(0)?);
8305 match self.peek() {
8306 Token::Comma => {
8307 self.advance();
8308 }
8309 Token::RBracket => break,
8310 other => {
8311 return Err(self.err(alloc::format!(
8312 "expected ',' or ']' in ARRAY literal, got {other:?}"
8313 )));
8314 }
8315 }
8316 }
8317 }
8318 self.advance(); Ok(Expr::Array(items))
8320 }
8321 Token::Ident(s) | Token::QuotedIdent(s)
8336 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
8337 {
8338 self.parse_match_against_atom()
8339 }
8340 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
8341 other if unreserved_keyword_text(&other).is_some() => {
8348 let s = unreserved_keyword_text(&other).unwrap();
8349 self.finish_ident_atom(s)
8350 }
8351 other => Err(ParseError {
8352 message: format!("unexpected token {other:?} in expression"),
8353 token_pos: tok_pos,
8354 }),
8355 }
8356 .and_then(|atom| self.finish_postfix_casts(atom))
8358 }
8359
8360 fn parse_cast_target(&mut self) -> Result<CastTarget, ParseError> {
8365 let target = match self.advance() {
8366 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
8367 "int" | "integer" | "int4" => {
8368 if matches!(self.peek(), Token::LBracket)
8369 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8370 {
8371 self.advance();
8372 self.advance();
8373 CastTarget::IntArray
8374 } else {
8375 CastTarget::Int
8376 }
8377 }
8378 "bigint" | "int8" => {
8379 if matches!(self.peek(), Token::LBracket)
8380 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8381 {
8382 self.advance();
8383 self.advance();
8384 CastTarget::BigIntArray
8385 } else {
8386 CastTarget::BigInt
8387 }
8388 }
8389 "float" | "double" | "real" => CastTarget::Float,
8390 "text" => {
8391 if matches!(self.peek(), Token::LBracket)
8393 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8394 {
8395 self.advance();
8396 self.advance();
8397 CastTarget::TextArray
8398 } else {
8399 CastTarget::Text
8400 }
8401 }
8402 "bool" | "boolean" => CastTarget::Bool,
8403 "vector" => CastTarget::Vector,
8404 "date" => CastTarget::Date,
8405 "timestamp" | "datetime" => CastTarget::Timestamp,
8406 "timestamptz" => CastTarget::Timestamptz,
8407 "interval" => CastTarget::Interval,
8408 "json" => CastTarget::Json,
8409 "jsonb" => CastTarget::Jsonb,
8410 "regtype" => CastTarget::RegType,
8411 "regclass" => CastTarget::RegClass,
8412 "tsvector" => CastTarget::TsVector,
8416 "tsquery" => CastTarget::TsQuery,
8417 "uuid" => CastTarget::Uuid,
8420 "bytea" => CastTarget::Bytea,
8425 other => {
8434 let mut name = other.to_string();
8440 if matches!(self.peek(), Token::LParen) {
8441 let mut buf = alloc::string::String::from("(");
8442 let mut depth = 0usize;
8443 loop {
8444 match self.advance() {
8445 Token::LParen => {
8446 depth += 1;
8447 if depth > 1 {
8448 buf.push('(');
8449 }
8450 }
8451 Token::RParen => {
8452 depth -= 1;
8453 if depth == 0 {
8454 buf.push(')');
8455 break;
8456 }
8457 buf.push(')');
8458 }
8459 Token::Comma => buf.push(','),
8460 Token::Integer(n) => buf.push_str(&alloc::format!("{n}")),
8461 Token::Eof => break,
8462 _ => {}
8463 }
8464 }
8465 name.push_str(&buf);
8466 }
8467 if matches!(self.peek(), Token::LBracket)
8472 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8473 {
8474 self.advance();
8475 self.advance();
8476 name.push_str("_array");
8477 }
8478 CastTarget::Named(name)
8479 }
8480 },
8481 Token::Interval => CastTarget::Interval,
8482 other => {
8483 return Err(ParseError {
8484 message: format!("expected type ident after `::`, got {other:?}"),
8485 token_pos: self.pos.saturating_sub(1),
8486 });
8487 }
8488 };
8489 if matches!(self.peek(), Token::LBracket)
8498 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
8499 {
8500 let widened = match &target {
8501 CastTarget::Bool => Some(CastTarget::Named("bool_array".to_string())),
8502 CastTarget::Date => Some(CastTarget::Named("date_array".to_string())),
8503 CastTarget::Timestamp | CastTarget::Timestamptz => {
8504 Some(CastTarget::Named("timestamptz_array".to_string()))
8505 }
8506 CastTarget::Uuid => Some(CastTarget::Named("uuid_array".to_string())),
8507 CastTarget::Json | CastTarget::Jsonb => {
8508 Some(CastTarget::Named("jsonb_array".to_string()))
8509 }
8510 CastTarget::Bytea => Some(CastTarget::Named("bytea_array".to_string())),
8511 CastTarget::Interval => Some(CastTarget::Named("interval_array".to_string())),
8512 CastTarget::Float => Some(CastTarget::Named("float_array".to_string())),
8513 CastTarget::Named(name) => {
8514 let mut a = name.clone();
8515 a.push_str("_array");
8516 Some(CastTarget::Named(a))
8517 }
8518 _ => None,
8522 };
8523 if let Some(w) = widened {
8524 self.advance();
8525 self.advance();
8526 return Ok(w);
8527 }
8528 }
8529 Ok(target)
8530 }
8531
8532 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
8533 loop {
8534 if matches!(self.peek(), Token::DoubleColon) {
8535 self.advance();
8536 let target = self.parse_cast_target()?;
8541 expr = Expr::Cast {
8542 expr: Box::new(expr),
8543 target,
8544 };
8545 continue;
8546 }
8547 if matches!(self.peek(), Token::Is) {
8548 self.advance();
8549 let negated = if matches!(self.peek(), Token::Not) {
8550 self.advance();
8551 true
8552 } else {
8553 false
8554 };
8555 if matches!(self.peek(), Token::Distinct) {
8558 self.advance();
8559 if !matches!(self.peek(), Token::From) {
8560 return Err(self.err(format!(
8561 "expected FROM after IS{} DISTINCT, got {:?}",
8562 if negated { " NOT" } else { "" },
8563 self.peek()
8564 )));
8565 }
8566 self.advance();
8567 let rhs = self.parse_expr(20)?;
8571 let op = if negated {
8572 BinOp::IsNotDistinctFrom
8573 } else {
8574 BinOp::IsDistinctFrom
8575 };
8576 expr = Expr::Binary {
8577 op,
8578 lhs: Box::new(expr),
8579 rhs: Box::new(rhs),
8580 };
8581 continue;
8582 }
8583 if !matches!(self.peek(), Token::Null) {
8584 return Err(self.err(format!(
8585 "expected NULL or DISTINCT after IS{}, got {:?}",
8586 if negated { " NOT" } else { "" },
8587 self.peek()
8588 )));
8589 }
8590 self.advance();
8591 expr = Expr::IsNull {
8592 expr: Box::new(expr),
8593 negated,
8594 };
8595 continue;
8596 }
8597 let negated = if matches!(self.peek(), Token::Not) {
8601 let next = self.tokens.get(self.pos + 1);
8602 matches!(next, Some(Token::Between | Token::In | Token::Like))
8603 || matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("ilike"))
8604 } else {
8605 false
8606 };
8607 if negated {
8608 self.advance();
8609 }
8610 if matches!(self.peek(), Token::Between) {
8611 expr = self.parse_between_tail(expr, negated)?;
8612 continue;
8613 }
8614 if matches!(self.peek(), Token::In) {
8615 expr = self.parse_in_tail(expr, negated)?;
8616 continue;
8617 }
8618 if matches!(self.peek(), Token::Like) {
8619 self.advance();
8620 let pattern = self.parse_expr(5)?;
8623 expr = Expr::Like {
8624 expr: Box::new(expr),
8625 pattern: Box::new(pattern),
8626 negated,
8627 case_insensitive: false,
8628 };
8629 continue;
8630 }
8631 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("ilike")) {
8634 self.advance();
8635 let pattern = self.parse_expr(5)?;
8636 expr = Expr::Like {
8637 expr: Box::new(expr),
8638 pattern: Box::new(pattern),
8639 negated,
8640 case_insensitive: true,
8641 };
8642 continue;
8643 }
8644 if matches!(self.peek(), Token::LBracket) {
8648 self.advance();
8649 let index = self.parse_expr(0)?;
8650 if !matches!(self.peek(), Token::RBracket) {
8651 return Err(self.err(alloc::format!(
8652 "expected ']' after array index, got {:?}",
8653 self.peek()
8654 )));
8655 }
8656 self.advance();
8657 expr = Expr::ArraySubscript {
8658 target: Box::new(expr),
8659 index: Box::new(index),
8660 };
8661 continue;
8662 }
8663 return Ok(expr);
8664 }
8665 }
8666
8667 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8671 self.advance(); let low = self.parse_expr(5)?;
8673 if !matches!(self.peek(), Token::And) {
8674 return Err(self.err(format!(
8675 "expected AND after BETWEEN low bound, got {:?}",
8676 self.peek()
8677 )));
8678 }
8679 self.advance();
8680 let high = self.parse_expr(5)?;
8681 let target = Box::new(expr);
8682 let combined = Expr::Binary {
8683 lhs: Box::new(Expr::Binary {
8684 lhs: target.clone(),
8685 op: BinOp::GtEq,
8686 rhs: Box::new(low),
8687 }),
8688 op: BinOp::And,
8689 rhs: Box::new(Expr::Binary {
8690 lhs: target,
8691 op: BinOp::LtEq,
8692 rhs: Box::new(high),
8693 }),
8694 };
8695 Ok(maybe_not(combined, negated))
8696 }
8697
8698 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
8703 let mut recursive = false;
8708 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8709 && s.eq_ignore_ascii_case("recursive")
8710 {
8711 self.advance();
8712 recursive = true;
8713 }
8714 let mut ctes = Vec::new();
8715 loop {
8716 let name = self.expect_ident_like()?;
8717 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
8721 self.advance();
8722 let mut names = Vec::new();
8723 loop {
8724 names.push(self.expect_ident_like()?);
8725 if matches!(self.peek(), Token::Comma) {
8726 self.advance();
8727 continue;
8728 }
8729 break;
8730 }
8731 if !matches!(self.peek(), Token::RParen) {
8732 return Err(self.err(format!(
8733 "expected ')' to close CTE column list, got {:?}",
8734 self.peek()
8735 )));
8736 }
8737 self.advance();
8738 names
8739 } else {
8740 Vec::new()
8741 };
8742 if !matches!(self.peek(), Token::As) {
8746 return Err(self.err(format!(
8747 "expected AS after CTE name {name:?}, got {:?}",
8748 self.peek()
8749 )));
8750 }
8751 self.advance();
8752 if !matches!(self.peek(), Token::LParen) {
8753 return Err(self.err(format!(
8754 "expected '(' after AS in WITH clause, got {:?}",
8755 self.peek()
8756 )));
8757 }
8758 self.advance();
8759 let is_update_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
8766 let is_delete_kw = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
8767 let body = match self.peek() {
8768 Token::Select => {
8769 let inner = self.parse_select_stmt()?;
8770 let Statement::Select(s) = inner else {
8771 unreachable!("parse_select_stmt returns Select");
8772 };
8773 crate::ast::CteBody::Select(s)
8774 }
8775 Token::Insert => {
8776 let inner = self.parse_one_statement()?;
8777 let Statement::Insert(s) = inner else {
8778 unreachable!("Token::Insert routes to Insert");
8779 };
8780 crate::ast::CteBody::Insert(alloc::boxed::Box::new(s))
8781 }
8782 _ if is_update_kw => {
8783 let inner = self.parse_one_statement()?;
8784 let Statement::Update(s) = inner else {
8785 return Err(
8786 self.err(format!("expected UPDATE inside WITH (…), got {inner:?}"))
8787 );
8788 };
8789 crate::ast::CteBody::Update(alloc::boxed::Box::new(s))
8790 }
8791 _ if is_delete_kw => {
8792 let inner = self.parse_one_statement()?;
8793 let Statement::Delete(s) = inner else {
8794 return Err(
8795 self.err(format!("expected DELETE inside WITH (…), got {inner:?}"))
8796 );
8797 };
8798 crate::ast::CteBody::Delete(alloc::boxed::Box::new(s))
8799 }
8800 other => {
8801 return Err(self.err(format!(
8802 "WITH body must be SELECT / INSERT / UPDATE / DELETE, got {other:?}"
8803 )));
8804 }
8805 };
8806 if !matches!(self.peek(), Token::RParen) {
8807 return Err(self.err(format!(
8808 "expected ')' after CTE body, got {:?}",
8809 self.peek()
8810 )));
8811 }
8812 self.advance();
8813 ctes.push(crate::ast::Cte {
8814 name,
8815 body,
8816 recursive,
8817 column_overrides,
8818 });
8819 if matches!(self.peek(), Token::Comma) {
8820 self.advance();
8821 continue;
8822 }
8823 break;
8824 }
8825 let outer_is_update = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update"));
8829 let outer_is_delete = matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete"));
8830 match self.peek() {
8831 Token::Select => {
8832 let body_stmt = self.parse_select_stmt()?;
8833 let Statement::Select(mut body) = body_stmt else {
8834 unreachable!()
8835 };
8836 body.ctes = ctes;
8837 Ok(Statement::Select(body))
8838 }
8839 Token::Insert => {
8840 let body_stmt = self.parse_one_statement()?;
8841 let Statement::Insert(mut body) = body_stmt else {
8842 unreachable!()
8843 };
8844 body.ctes = ctes;
8845 Ok(Statement::Insert(body))
8846 }
8847 _ if outer_is_update => {
8848 let body_stmt = self.parse_one_statement()?;
8849 let Statement::Update(mut body) = body_stmt else {
8850 return Err(self.err(format!("expected UPDATE after WITH clause")));
8851 };
8852 body.ctes = ctes;
8853 Ok(Statement::Update(body))
8854 }
8855 _ if outer_is_delete => {
8856 let body_stmt = self.parse_one_statement()?;
8857 let Statement::Delete(mut body) = body_stmt else {
8858 return Err(self.err(format!("expected DELETE after WITH clause")));
8859 };
8860 body.ctes = ctes;
8861 Ok(Statement::Delete(body))
8862 }
8863 other => Err(self.err(format!(
8864 "expected SELECT / INSERT / UPDATE / DELETE after WITH clause, got {other:?}"
8865 ))),
8866 }
8867 }
8868
8869 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
8878 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
8882 None
8883 } else {
8884 Some(Box::new(self.parse_expr(0)?))
8885 };
8886 let mut branches: Vec<(Expr, Expr)> = Vec::new();
8887 loop {
8888 match self.peek() {
8889 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
8890 self.advance();
8891 let cond = self.parse_expr(0)?;
8892 match self.peek() {
8893 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
8894 self.advance();
8895 }
8896 other => {
8897 return Err(self.err(alloc::format!(
8898 "expected THEN after CASE WHEN <expr>, got {other:?}"
8899 )));
8900 }
8901 }
8902 let value = self.parse_expr(0)?;
8903 branches.push((cond, value));
8904 }
8905 _ => break,
8906 }
8907 }
8908 if branches.is_empty() {
8909 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
8910 }
8911 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
8912 {
8913 self.advance();
8914 Some(Box::new(self.parse_expr(0)?))
8915 } else {
8916 None
8917 };
8918 match self.peek() {
8919 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
8920 self.advance();
8921 }
8922 other => {
8923 return Err(self.err(alloc::format!(
8924 "expected END to close CASE expression, got {other:?}"
8925 )));
8926 }
8927 }
8928 Ok(Expr::Case {
8929 operand,
8930 branches,
8931 else_branch,
8932 })
8933 }
8934
8935 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
8936 if !matches!(self.peek(), Token::LParen) {
8937 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
8938 }
8939 self.advance();
8940 let inner = self.parse_select_stmt()?;
8941 if !matches!(self.peek(), Token::RParen) {
8942 return Err(self.err(format!(
8943 "expected ')' after EXISTS-subquery, got {:?}",
8944 self.peek()
8945 )));
8946 }
8947 self.advance();
8948 let Statement::Select(s) = inner else {
8949 unreachable!("parse_select_stmt returns Select")
8950 };
8951 Ok(Expr::Exists {
8952 subquery: Box::new(s),
8953 negated,
8954 })
8955 }
8956
8957 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8958 self.advance(); if !matches!(self.peek(), Token::LParen) {
8960 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
8961 }
8962 self.advance();
8963 if matches!(self.peek(), Token::Select) {
8965 let inner = self.parse_select_stmt()?;
8966 if !matches!(self.peek(), Token::RParen) {
8967 return Err(self.err(format!(
8968 "expected ')' after IN-subquery, got {:?}",
8969 self.peek()
8970 )));
8971 }
8972 self.advance();
8973 let Statement::Select(s) = inner else {
8974 unreachable!("parse_select_stmt always returns Statement::Select")
8975 };
8976 return Ok(Expr::InSubquery {
8977 expr: Box::new(expr),
8978 subquery: Box::new(s),
8979 negated,
8980 });
8981 }
8982 let mut elements = Vec::new();
8983 if !matches!(self.peek(), Token::RParen) {
8984 loop {
8985 elements.push(self.parse_expr(0)?);
8986 match self.peek() {
8987 Token::Comma => {
8988 self.advance();
8989 }
8990 Token::RParen => break,
8991 other => {
8992 return Err(
8993 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
8994 );
8995 }
8996 }
8997 }
8998 }
8999 self.advance(); if elements.is_empty() {
9004 return Ok(maybe_not(Expr::Literal(Literal::Bool(false)), negated));
9005 }
9006 Ok(Expr::InList {
9007 expr: Box::new(expr),
9008 list: elements,
9009 negated,
9010 })
9011 }
9012
9013 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
9034 if !matches!(self.peek(), Token::LParen) {
9037 return Err(self.err(alloc::format!(
9038 "expected '(' after MATCH, got {:?}",
9039 self.peek()
9040 )));
9041 }
9042 self.advance();
9043 let mut cols: Vec<Expr> = Vec::new();
9044 loop {
9045 cols.push(self.parse_expr(0)?);
9046 match self.peek() {
9047 Token::Comma => {
9048 self.advance();
9049 }
9050 Token::RParen => break,
9051 other => {
9052 return Err(self.err(alloc::format!(
9053 "expected ',' or ')' in MATCH column list, got {other:?}"
9054 )));
9055 }
9056 }
9057 }
9058 self.advance(); match self.peek() {
9061 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
9062 self.advance();
9063 }
9064 other => {
9065 return Err(self.err(alloc::format!(
9066 "expected AGAINST after MATCH column list, got {other:?}"
9067 )));
9068 }
9069 }
9070 if !matches!(self.peek(), Token::LParen) {
9071 return Err(self.err(alloc::format!(
9072 "expected '(' after AGAINST, got {:?}",
9073 self.peek()
9074 )));
9075 }
9076 self.advance();
9077 let term = match self.advance() {
9087 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
9088 Token::Placeholder(n) => Expr::Placeholder(n),
9089 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
9090 qualifier: None,
9091 name: s,
9092 }),
9093 other => {
9094 return Err(self.err(alloc::format!(
9095 "MATCH ... AGAINST(<term>) expects a string literal, \
9096 bound parameter, or column ref, got {other:?}"
9097 )));
9098 }
9099 };
9100 loop {
9105 match self.peek() {
9106 Token::In => {
9109 self.advance();
9110 }
9111 Token::Ident(s) | Token::QuotedIdent(s)
9112 if s.eq_ignore_ascii_case("natural")
9113 || s.eq_ignore_ascii_case("language")
9114 || s.eq_ignore_ascii_case("boolean")
9115 || s.eq_ignore_ascii_case("mode")
9116 || s.eq_ignore_ascii_case("with")
9117 || s.eq_ignore_ascii_case("query")
9118 || s.eq_ignore_ascii_case("expansion") =>
9119 {
9120 self.advance();
9121 }
9122 _ => break,
9123 }
9124 }
9125 if !matches!(self.peek(), Token::RParen) {
9126 return Err(self.err(alloc::format!(
9127 "expected ')' to close AGAINST, got {:?}",
9128 self.peek()
9129 )));
9130 }
9131 self.advance();
9132 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
9135 let plainto = Expr::FunctionCall {
9136 name: String::from("plainto_tsquery"),
9137 args: alloc::vec![simple_lit(), term.clone()],
9138 };
9139 let mut folded: Option<Expr> = None;
9140 for col in cols {
9141 let to_tsv = Expr::FunctionCall {
9142 name: String::from("to_tsvector"),
9143 args: alloc::vec![simple_lit(), col],
9144 };
9145 let leaf = Expr::Binary {
9146 lhs: Box::new(to_tsv),
9147 op: crate::ast::BinOp::TsMatch,
9148 rhs: Box::new(plainto.clone()),
9149 };
9150 folded = Some(match folded {
9151 None => leaf,
9152 Some(prev) => Expr::Binary {
9153 lhs: Box::new(prev),
9154 op: crate::ast::BinOp::Or,
9155 rhs: Box::new(leaf),
9156 },
9157 });
9158 }
9159 match folded {
9160 Some(e) => Ok(e),
9161 None => Err(self.err(String::from(
9162 "MATCH(...) AGAINST(...) requires at least one column",
9163 ))),
9164 }
9165 }
9166
9167 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
9168 if !matches!(self.peek(), Token::LParen) {
9169 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
9170 }
9171 self.advance();
9172 let field_name = self.expect_ident_like()?;
9173 let field = match field_name.to_ascii_lowercase().as_str() {
9174 "year" => ExtractField::Year,
9175 "month" => ExtractField::Month,
9176 "day" => ExtractField::Day,
9177 "hour" => ExtractField::Hour,
9178 "minute" => ExtractField::Minute,
9179 "second" => ExtractField::Second,
9180 "microsecond" | "microseconds" => ExtractField::Microsecond,
9181 "epoch" => ExtractField::Epoch,
9182 other => {
9183 return Err(self.err(format!(
9184 "unknown EXTRACT field {other:?}; \
9185 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
9186 )));
9187 }
9188 };
9189 if !matches!(self.peek(), Token::From) {
9190 return Err(self.err(format!(
9191 "expected FROM after EXTRACT field, got {:?}",
9192 self.peek()
9193 )));
9194 }
9195 self.advance();
9196 let source = self.parse_expr(0)?;
9197 if !matches!(self.peek(), Token::RParen) {
9198 return Err(self.err(format!(
9199 "expected ')' to close EXTRACT, got {:?}",
9200 self.peek()
9201 )));
9202 }
9203 self.advance();
9204 Ok(Expr::Extract {
9205 field,
9206 source: Box::new(source),
9207 })
9208 }
9209
9210 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
9215 let tok = self.advance();
9216 let Token::String(text) = tok else {
9217 return Err(self.err(format!(
9218 "expected string literal after INTERVAL, got {tok:?}"
9219 )));
9220 };
9221 let (months, days, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
9222 message: format!(
9223 "cannot parse INTERVAL {text:?}; \
9224 expected `<n> <unit> [<n> <unit> ...]` with units \
9225 microsecond[s], millisecond[s], second[s], minute[s], \
9226 hour[s], day[s], week[s], month[s], year[s]"
9227 ),
9228 token_pos: self.pos.saturating_sub(1),
9229 })?;
9230 Ok(Expr::Literal(Literal::Interval {
9231 months,
9232 days,
9233 micros,
9234 text,
9235 }))
9236 }
9237
9238 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
9239 let mut elems = Vec::new();
9240 if matches!(self.peek(), Token::RBracket) {
9241 self.advance();
9242 return Ok(Expr::Literal(Literal::Vector(elems)));
9243 }
9244 loop {
9245 let e = self.parse_expr(0)?;
9246 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
9247 message: format!("vector element must be a numeric literal, got {e:?}"),
9248 token_pos: self.pos,
9249 })?;
9250 elems.push(x);
9251 match self.peek() {
9252 Token::Comma => {
9253 self.advance();
9254 }
9255 Token::RBracket => {
9256 self.advance();
9257 break;
9258 }
9259 other => {
9260 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
9261 }
9262 }
9263 }
9264 Ok(Expr::Literal(Literal::Vector(elems)))
9265 }
9266
9267 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
9276 let Token::Ident(s) = self.peek().clone() else {
9277 return NullTreatment::Respect;
9278 };
9279 let is_ignore = s.eq_ignore_ascii_case("ignore");
9280 let is_respect = s.eq_ignore_ascii_case("respect");
9281 if !is_ignore && !is_respect {
9282 return NullTreatment::Respect;
9283 }
9284 if self.pos + 1 < self.tokens.len()
9287 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
9288 && s2.eq_ignore_ascii_case("nulls")
9289 {
9290 self.advance();
9291 self.advance();
9292 return if is_ignore {
9293 NullTreatment::Ignore
9294 } else {
9295 NullTreatment::Respect
9296 };
9297 }
9298 NullTreatment::Respect
9299 }
9300
9301 fn parse_filter_clause(&mut self) -> Result<Option<Box<Expr>>, ParseError> {
9306 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9307 return Ok(None);
9308 };
9309 if !s.eq_ignore_ascii_case("filter") {
9310 return Ok(None);
9311 }
9312 self.advance(); if !matches!(self.peek(), Token::LParen) {
9314 return Err(self.err(format!("expected '(' after FILTER, got {:?}", self.peek())));
9315 }
9316 self.advance(); if !matches!(self.peek(), Token::Where) {
9318 return Err(self.err(format!(
9319 "expected WHERE inside FILTER (...), got {:?}",
9320 self.peek()
9321 )));
9322 }
9323 self.advance(); let cond = self.parse_expr(0)?;
9325 if !matches!(self.peek(), Token::RParen) {
9326 return Err(self.err(format!(
9327 "expected ')' to close FILTER (WHERE ...), got {:?}",
9328 self.peek()
9329 )));
9330 }
9331 self.advance(); Ok(Some(Box::new(cond)))
9333 }
9334
9335 fn parse_within_group_clause(&mut self) -> Result<Vec<OrderBy>, ParseError> {
9340 let (Token::Ident(s) | Token::QuotedIdent(s)) = self.peek() else {
9341 return Ok(Vec::new());
9342 };
9343 if !s.eq_ignore_ascii_case("within") {
9344 return Ok(Vec::new());
9345 }
9346 self.advance(); if !matches!(self.peek(), Token::Group) {
9348 return Err(self.err(format!(
9349 "expected GROUP after WITHIN, got {:?}",
9350 self.peek()
9351 )));
9352 }
9353 self.advance(); if !matches!(self.peek(), Token::LParen) {
9355 return Err(self.err(format!(
9356 "expected '(' after WITHIN GROUP, got {:?}",
9357 self.peek()
9358 )));
9359 }
9360 self.advance(); if !matches!(self.peek(), Token::Order) {
9362 return Err(self.err(format!(
9363 "expected ORDER BY inside WITHIN GROUP (...), got {:?}",
9364 self.peek()
9365 )));
9366 }
9367 self.advance(); if !matches!(self.peek(), Token::By) {
9369 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9370 }
9371 self.advance(); let mut keys: Vec<OrderBy> = Vec::new();
9373 loop {
9374 let expr = self.parse_expr(0)?;
9375 let desc = if matches!(self.peek(), Token::Desc) {
9376 self.advance();
9377 true
9378 } else if matches!(self.peek(), Token::Asc) {
9379 self.advance();
9380 false
9381 } else {
9382 false
9383 };
9384 let nulls_first = self.parse_optional_nulls_placement()?;
9385 keys.push(OrderBy {
9386 expr,
9387 desc,
9388 nulls_first,
9389 });
9390 if matches!(self.peek(), Token::Comma) {
9391 self.advance();
9392 } else {
9393 break;
9394 }
9395 }
9396 if !matches!(self.peek(), Token::RParen) {
9397 return Err(self.err(format!(
9398 "expected ')' to close WITHIN GROUP (ORDER BY ...), got {:?}",
9399 self.peek()
9400 )));
9401 }
9402 self.advance(); Ok(keys)
9404 }
9405
9406 #[allow(clippy::type_complexity)] fn parse_over_clause(
9409 &mut self,
9410 ) -> Result<
9411 (
9412 Vec<Expr>,
9413 Vec<(Expr, bool, Option<bool>)>,
9414 Option<WindowFrame>,
9415 ),
9416 ParseError,
9417 > {
9418 if !matches!(self.peek(), Token::LParen) {
9419 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
9420 }
9421 self.advance();
9422 let mut partition_by = Vec::new();
9423 let mut order_by = Vec::new();
9424 let is_partition_kw = match self.peek() {
9430 Token::Partition => true,
9431 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("partition"),
9432 _ => false,
9433 };
9434 if is_partition_kw {
9435 self.advance();
9436 if !matches!(self.peek(), Token::By) {
9437 return Err(self.err(format!(
9438 "expected BY after PARTITION, got {:?}",
9439 self.peek()
9440 )));
9441 }
9442 self.advance();
9443 loop {
9444 partition_by.push(self.parse_expr(0)?);
9445 if matches!(self.peek(), Token::Comma) {
9446 self.advance();
9447 continue;
9448 }
9449 break;
9450 }
9451 }
9452 if matches!(self.peek(), Token::Order) {
9454 self.advance();
9455 if !matches!(self.peek(), Token::By) {
9456 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
9457 }
9458 self.advance();
9459 loop {
9460 let e = self.parse_expr(0)?;
9461 let desc = if matches!(self.peek(), Token::Desc) {
9462 self.advance();
9463 true
9464 } else if matches!(self.peek(), Token::Asc) {
9465 self.advance();
9466 false
9467 } else {
9468 false
9469 };
9470 let nulls_first = self.parse_optional_nulls_placement()?;
9472 order_by.push((e, desc, nulls_first));
9473 if matches!(self.peek(), Token::Comma) {
9474 self.advance();
9475 continue;
9476 }
9477 break;
9478 }
9479 }
9480 let mut frame: Option<WindowFrame> = None;
9484 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
9485 let kind = if s.eq_ignore_ascii_case("rows") {
9486 Some(FrameKind::Rows)
9487 } else if s.eq_ignore_ascii_case("range") {
9488 Some(FrameKind::Range)
9489 } else {
9490 None
9491 };
9492 if let Some(kind) = kind {
9493 self.advance();
9494 frame = Some(self.parse_frame_tail(kind)?);
9495 }
9496 }
9497 if !matches!(self.peek(), Token::RParen) {
9498 return Err(self.err(format!(
9499 "expected ')' to close OVER clause, got {:?}",
9500 self.peek()
9501 )));
9502 }
9503 self.advance();
9504 Ok((partition_by, order_by, frame))
9505 }
9506
9507 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
9513 if matches!(self.peek(), Token::Between) {
9514 self.advance();
9515 let start = self.parse_frame_bound()?;
9516 if !matches!(self.peek(), Token::And) {
9517 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
9518 }
9519 self.advance();
9520 let end = self.parse_frame_bound()?;
9521 Ok(WindowFrame {
9522 kind,
9523 start,
9524 end: Some(end),
9525 })
9526 } else {
9527 let start = self.parse_frame_bound()?;
9528 Ok(WindowFrame {
9529 kind,
9530 start,
9531 end: None,
9532 })
9533 }
9534 }
9535
9536 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
9539 if let Token::Integer(n) = *self.peek() {
9541 self.advance();
9542 let n: u64 = u64::try_from(n).map_err(|_| {
9543 self.err(format!(
9544 "invalid frame offset {n} — expected non-negative integer"
9545 ))
9546 })?;
9547 let dir = self.expect_ident_like()?;
9548 return if dir.eq_ignore_ascii_case("preceding") {
9549 Ok(FrameBound::OffsetPreceding(n))
9550 } else if dir.eq_ignore_ascii_case("following") {
9551 Ok(FrameBound::OffsetFollowing(n))
9552 } else {
9553 Err(self.err(format!(
9554 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
9555 )))
9556 };
9557 }
9558 let first = self.expect_ident_like()?;
9559 if first.eq_ignore_ascii_case("unbounded") {
9560 let dir = self.expect_ident_like()?;
9561 return if dir.eq_ignore_ascii_case("preceding") {
9562 Ok(FrameBound::UnboundedPreceding)
9563 } else if dir.eq_ignore_ascii_case("following") {
9564 Ok(FrameBound::UnboundedFollowing)
9565 } else {
9566 Err(self.err(format!(
9567 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
9568 )))
9569 };
9570 }
9571 if first.eq_ignore_ascii_case("current") {
9572 let row = self.expect_ident_like()?;
9573 if !row.eq_ignore_ascii_case("row") {
9574 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
9575 }
9576 return Ok(FrameBound::CurrentRow);
9577 }
9578 Err(self.err(format!(
9579 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
9580 )))
9581 }
9582
9583 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
9584 if matches!(self.peek(), Token::Dot) {
9585 self.advance();
9586 let name = self.expect_ident_like()?;
9587 if matches!(self.peek(), Token::LParen) {
9593 return self.finish_ident_atom(name);
9594 }
9595 return Ok(Expr::Column(ColumnName {
9596 qualifier: Some(first),
9597 name,
9598 }));
9599 }
9600 if matches!(self.peek(), Token::LParen) {
9601 self.advance();
9602 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
9606 self.advance();
9607 if !matches!(self.peek(), Token::RParen) {
9608 return Err(self.err(format!(
9609 "expected ')' after COUNT(*), got {:?}",
9610 self.peek()
9611 )));
9612 }
9613 self.advance();
9614 let filter = self.parse_filter_clause()?;
9616 let null_treatment = self.parse_null_treatment_modifier();
9618 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9619 && s.eq_ignore_ascii_case("over")
9620 {
9621 if filter.is_some() {
9622 return Err(
9623 self.err("FILTER on window functions is not supported yet".into())
9624 );
9625 }
9626 self.advance();
9627 let (partition_by, order_by, frame) = self.parse_over_clause()?;
9628 return Ok(Expr::WindowFunction {
9629 name: "count_star".into(),
9630 args: Vec::new(),
9631 partition_by,
9632 order_by,
9633 frame,
9634 null_treatment,
9635 });
9636 }
9637 if let Some(filter) = filter {
9638 return Ok(Expr::AggregateOrdered {
9639 call: Box::new(Expr::FunctionCall {
9640 name: "count_star".into(),
9641 args: Vec::new(),
9642 }),
9643 order_by: Vec::new(),
9644 distinct: false,
9645 filter: Some(filter),
9646 });
9647 }
9648 return Ok(Expr::FunctionCall {
9649 name: "count_star".into(),
9650 args: Vec::new(),
9651 });
9652 }
9653 let mut args = Vec::new();
9655 let mut agg_order_by: Vec<OrderBy> = Vec::new();
9656 let agg_distinct = if matches!(self.peek(), Token::Distinct) {
9660 self.advance();
9661 true
9662 } else if matches!(self.peek(), Token::All) {
9663 self.advance();
9664 false
9665 } else {
9666 false
9667 };
9668 if !matches!(self.peek(), Token::RParen) {
9669 loop {
9670 args.push(self.parse_expr(0)?);
9671 if first.eq_ignore_ascii_case("cast")
9675 && args.len() == 1
9676 && matches!(self.peek(), Token::As)
9677 {
9678 self.advance();
9679 let target = self.parse_cast_target()?;
9680 if !matches!(self.peek(), Token::RParen) {
9681 return Err(self.err(format!(
9682 "expected ')' to close CAST, got {:?}",
9683 self.peek()
9684 )));
9685 }
9686 self.advance();
9687 return Ok(Expr::Cast {
9688 expr: Box::new(args.pop().expect("one arg")),
9689 target,
9690 });
9691 }
9692 if matches!(self.peek(), Token::Order) {
9696 self.advance();
9697 if !matches!(self.peek(), Token::By) {
9698 return Err(self.err(format!(
9699 "expected BY after ORDER in aggregate args, got {:?}",
9700 self.peek()
9701 )));
9702 }
9703 self.advance();
9704 loop {
9705 let expr = self.parse_expr(0)?;
9706 let desc = if matches!(self.peek(), Token::Desc) {
9707 self.advance();
9708 true
9709 } else if matches!(self.peek(), Token::Asc) {
9710 self.advance();
9711 false
9712 } else {
9713 false
9714 };
9715 let nulls_first = self.parse_optional_nulls_placement()?;
9716 agg_order_by.push(OrderBy {
9717 expr,
9718 desc,
9719 nulls_first,
9720 });
9721 if matches!(self.peek(), Token::Comma) {
9722 self.advance();
9723 } else {
9724 break;
9725 }
9726 }
9727 if !matches!(self.peek(), Token::RParen) {
9728 return Err(self.err(format!(
9729 "expected ')' after aggregate ORDER BY, got {:?}",
9730 self.peek()
9731 )));
9732 }
9733 break;
9734 }
9735 match self.peek() {
9736 Token::Comma => {
9737 self.advance();
9738 }
9739 Token::RParen => break,
9740 other => {
9741 return Err(self.err(format!(
9742 "expected ',' or ')' in function args, got {other:?}"
9743 )));
9744 }
9745 }
9746 }
9747 }
9748 self.advance(); let within_group_order = self.parse_within_group_clause()?;
9757 if !within_group_order.is_empty() && !agg_order_by.is_empty() {
9758 return Err(self.err(
9759 "an aggregate may not carry both an in-argument ORDER BY and WITHIN GROUP"
9760 .into(),
9761 ));
9762 }
9763 let agg_order_by = if within_group_order.is_empty() {
9764 agg_order_by
9765 } else {
9766 within_group_order
9767 };
9768 let filter = self.parse_filter_clause()?;
9770 let null_treatment = self.parse_null_treatment_modifier();
9777 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
9778 && s.eq_ignore_ascii_case("over")
9779 {
9780 if filter.is_some() {
9781 return Err(self.err("FILTER on window functions is not supported yet".into()));
9782 }
9783 self.advance();
9784 let (partition_by, order_by, frame) = self.parse_over_clause()?;
9785 return Ok(Expr::WindowFunction {
9786 name: first,
9787 args,
9788 partition_by,
9789 order_by,
9790 frame,
9791 null_treatment,
9792 });
9793 }
9794 if !agg_order_by.is_empty() || agg_distinct || filter.is_some() {
9795 return Ok(Expr::AggregateOrdered {
9796 call: Box::new(Expr::FunctionCall { name: first, args }),
9797 order_by: agg_order_by,
9798 distinct: agg_distinct,
9799 filter,
9800 });
9801 }
9802 return Ok(Expr::FunctionCall { name: first, args });
9803 }
9804 let lc = first.to_ascii_lowercase();
9810 if matches!(
9811 lc.as_str(),
9812 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
9813 ) {
9814 return Ok(Expr::FunctionCall {
9815 name: lc,
9816 args: Vec::new(),
9817 });
9818 }
9819 Ok(Expr::Column(ColumnName {
9820 qualifier: None,
9821 name: first,
9822 }))
9823 }
9824}
9825
9826fn extract_first_column(expr: &Expr) -> Option<String> {
9834 match expr {
9835 Expr::Column(cn) => Some(cn.name.clone()),
9836 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
9837 Expr::Binary { lhs, rhs, .. } => {
9838 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
9839 }
9840 Expr::Unary { expr: e, .. } => extract_first_column(e),
9841 _ => None,
9842 }
9843}
9844
9845fn maybe_not(expr: Expr, negated: bool) -> Expr {
9846 if negated {
9847 Expr::Unary {
9848 op: UnOp::Not,
9849 expr: Box::new(expr),
9850 }
9851 } else {
9852 expr
9853 }
9854}
9855
9856fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
9857 let pair = match tok {
9858 Token::Or => (BinOp::Or, 1),
9859 Token::And => (BinOp::And, 2),
9860 Token::Eq => (BinOp::Eq, 4),
9861 Token::NotEq => (BinOp::NotEq, 4),
9862 Token::Lt => (BinOp::Lt, 4),
9863 Token::LtEq => (BinOp::LtEq, 4),
9864 Token::Gt => (BinOp::Gt, 4),
9865 Token::GtEq => (BinOp::GtEq, 4),
9866 Token::L2Distance => (BinOp::L2Distance, 5),
9869 Token::InnerProduct => (BinOp::InnerProduct, 5),
9870 Token::CosineDistance => (BinOp::CosineDistance, 5),
9871 Token::Plus => (BinOp::Add, 6),
9872 Token::Minus => (BinOp::Sub, 6),
9873 Token::Concat => (BinOp::Concat, 6),
9876 Token::Pipe => (BinOp::BitOr, 6),
9889 Token::Amp => (BinOp::BitAnd, 6),
9890 Token::Star => (BinOp::Mul, 7),
9891 Token::Slash => (BinOp::Div, 7),
9892 Token::JsonGet => (BinOp::JsonGet, 7),
9896 Token::JsonGetText => (BinOp::JsonGetText, 7),
9897 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
9898 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
9899 Token::JsonContains => (BinOp::JsonContains, 7),
9900 Token::JsonContainedBy => (BinOp::JsonContainedBy, 7),
9901 Token::JsonKeyExists => (BinOp::JsonKeyExists, 7),
9902 Token::JsonKeysAny => (BinOp::JsonKeysAny, 7),
9903 Token::JsonKeysAll => (BinOp::JsonKeysAll, 7),
9904 Token::TsMatch => (BinOp::TsMatch, 4),
9908 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
9912 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
9913 Token::InetContains => (BinOp::InetContains, 4),
9914 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
9915 Token::InetOverlap => (BinOp::InetOverlap, 4),
9916 _ => return None,
9917 };
9918 Some(pair)
9919}
9920
9921#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
9922fn is_alias_stopword(s: &str) -> bool {
9934 matches!(
9935 s.to_ascii_lowercase().as_str(),
9936 "with"
9937 | "on"
9938 | "where"
9939 | "having"
9940 | "group"
9941 | "order"
9942 | "limit"
9943 | "offset"
9944 | "union"
9945 | "except"
9946 | "intersect"
9947 | "returning"
9948 | "set"
9949 | "values"
9950 | "for"
9951 | "lateral"
9952 | "left"
9953 | "right"
9954 | "inner"
9955 | "outer"
9956 | "full"
9957 | "cross"
9958 | "join"
9959 | "natural"
9960 | "using"
9961 | "fetch"
9962 )
9963}
9964
9965fn extract_numeric_literal(e: &Expr) -> Option<f32> {
9966 match e {
9967 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
9968 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
9969 Expr::Unary {
9970 op: UnOp::Neg,
9971 expr,
9972 } => extract_numeric_literal(expr).map(|x| -x),
9973 _ => None,
9974 }
9975}
9976
9977pub fn parse_interval_text(s: &str) -> Option<(i32, i32, i64)> {
9989 let parts: Vec<&str> = s.split_whitespace().collect();
9990 if parts.is_empty() || !parts.len().is_multiple_of(2) {
9991 return None;
9992 }
9993 let mut months: i32 = 0;
9994 let mut days: i32 = 0;
9995 let mut micros: i64 = 0;
9996 let mut i = 0;
9997 while i < parts.len() {
9998 let n: i64 = parts[i].parse().ok()?;
9999 let unit = parts[i + 1].to_ascii_lowercase();
10000 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
10001 match unit_stripped {
10002 "microsecond" => micros = micros.checked_add(n)?,
10003 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
10004 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
10005 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
10006 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
10007 "day" => {
10008 let n32 = i32::try_from(n).ok()?;
10009 days = days.checked_add(n32)?;
10010 }
10011 "week" => {
10012 let n32 = i32::try_from(n).ok()?;
10013 days = days.checked_add(n32.checked_mul(7)?)?;
10014 }
10015 "month" | "mon" => {
10019 let n32 = i32::try_from(n).ok()?;
10020 months = months.checked_add(n32)?;
10021 }
10022 "year" => {
10023 let n32 = i32::try_from(n).ok()?;
10024 months = months.checked_add(n32.checked_mul(12)?)?;
10025 }
10026 _ => return None,
10027 }
10028 i += 2;
10029 }
10030 Some((months, days, micros))
10031}
10032
10033fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
10044 Some(match ident.to_ascii_lowercase().as_str() {
10045 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
10046 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
10047 "bigint" => ColumnTypeName::BigInt,
10048 "float" | "double" | "real" => ColumnTypeName::Float,
10049 "text" => ColumnTypeName::Text,
10050 "bool" | "boolean" => ColumnTypeName::Bool,
10051 "date" => ColumnTypeName::Date,
10052 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
10053 "timestamptz" => ColumnTypeName::Timestamptz,
10054 "json" => ColumnTypeName::Json,
10055 "jsonb" => ColumnTypeName::Jsonb,
10056 "bytea" | "bytes" => ColumnTypeName::Bytes,
10057 "tsvector" => ColumnTypeName::TsVector,
10058 "tsquery" => ColumnTypeName::TsQuery,
10059 "uuid" => ColumnTypeName::Uuid,
10060 "interval" => ColumnTypeName::Interval,
10061 "time" => ColumnTypeName::Time,
10062 "year" => ColumnTypeName::Year,
10063 "timetz" => ColumnTypeName::TimeTz,
10064 "money" => ColumnTypeName::Money,
10065 _ => return None,
10066 })
10067}
10068
10069pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10096 parse_plpgsql_body(body)
10097}
10098
10099fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
10100 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
10104 message: alloc::format!("plpgsql body lex error: {e}"),
10105 token_pos: 0,
10106 })?;
10107 let mut parser = Parser::new(tokens);
10108 parser.parse_plpgsql_block()
10109}
10110
10111#[cfg(test)]
10112mod tests {
10113 use super::*;
10114 use alloc::string::ToString;
10115
10116 fn parse(s: &str) -> Statement {
10117 parse_statement(s).expect("parse ok")
10118 }
10119
10120 #[test]
10130 fn release_usable_as_column_name_in_create_table() {
10131 let stmt =
10132 parse("CREATE TABLE events (id INT PRIMARY KEY, release TEXT NOT NULL, payload TEXT)");
10133 if let Statement::CreateTable(t) = stmt {
10134 let names: alloc::vec::Vec<&str> = t.columns.iter().map(|c| c.name.as_str()).collect();
10135 assert_eq!(names, alloc::vec!["id", "release", "payload"]);
10136 } else {
10137 panic!("expected CreateTable");
10138 }
10139 }
10140
10141 #[test]
10142 fn release_usable_as_column_ref_in_select_projection() {
10143 parse("SELECT id, release, payload FROM events WHERE id = 1");
10148 }
10149
10150 #[test]
10151 fn release_usable_as_column_ref_in_insert_column_list() {
10152 parse("INSERT INTO events (id, release, payload) VALUES (1, '1.0.0', 'data')");
10154 }
10155
10156 #[test]
10157 fn alter_column_drop_not_null_uses_keyword_drop_token() {
10158 parse("ALTER TABLE audit_logs ALTER COLUMN org_id DROP NOT NULL");
10163 }
10164
10165 #[test]
10166 fn create_index_accepts_parenthesised_expression_key() {
10167 parse(
10173 "CREATE INDEX IF NOT EXISTS events_bundle_id_idx \
10174 ON events ((payload->'bundle'->>'id'))",
10175 );
10176 }
10177
10178 #[test]
10182 fn nesting_budget_errors_cleanly() {
10183 let depth = MAX_NEST_DEPTH + 50;
10184 let sql = format!("SELECT {}1{}", "(".repeat(depth), ")".repeat(depth));
10185 let err = parse_statement(&sql).expect_err("must reject");
10186 assert!(err.message.contains("nests deeper"), "{err:?}");
10187 let sql = format!("SELECT {}1{}", "(".repeat(48), ")".repeat(48));
10189 parse(&sql);
10190 }
10191
10192 #[test]
10193 fn binary_chain_budget_errors_cleanly() {
10194 let sql = format!("SELECT 1{}", " + 1".repeat(MAX_BINARY_CHAIN + 50));
10195 let err = parse_statement(&sql).expect_err("must reject");
10196 assert!(err.message.contains("chained binary"), "{err:?}");
10197 let sql = format!("SELECT 1{}", " + 1".repeat(200));
10200 parse(&sql);
10201 }
10202
10203 #[test]
10204 fn in_list_unaffected_by_chain_budget() {
10205 let items: alloc::vec::Vec<String> = (0..20_000).map(|k| k.to_string()).collect();
10207 let sql = format!("SELECT 1 WHERE 5 IN ({})", items.join(","));
10208 let Statement::Select(s) = parse(&sql) else {
10209 panic!("expected select")
10210 };
10211 let Some(Expr::InList { list, negated, .. }) = s.where_ else {
10212 panic!("expected flat InList, got {:?}", s.where_)
10213 };
10214 assert_eq!(list.len(), 20_000);
10215 assert!(!negated);
10216 }
10217
10218 fn lit_int(n: i64) -> Expr {
10219 Expr::Literal(Literal::Integer(n))
10220 }
10221
10222 fn col(name: &str) -> Expr {
10223 Expr::Column(ColumnName {
10224 qualifier: None,
10225 name: name.into(),
10226 })
10227 }
10228
10229 #[test]
10230 fn select_single_integer() {
10231 let s = parse("SELECT 1");
10232 let Statement::Select(s) = s else {
10233 panic!("expected SELECT")
10234 };
10235 assert_eq!(s.items.len(), 1);
10236 assert!(s.from.is_none());
10237 assert!(s.where_.is_none());
10238 }
10239
10240 #[test]
10241 fn select_multiple_literal_kinds() {
10242 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
10243 let Statement::Select(s) = s else {
10244 panic!("expected SELECT")
10245 };
10246 assert_eq!(s.items.len(), 5);
10247 }
10248
10249 #[test]
10250 fn select_wildcard_from_table() {
10251 let s = parse("SELECT * FROM users");
10252 let Statement::Select(s) = s else {
10253 panic!("expected SELECT")
10254 };
10255 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
10256 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
10257 }
10258
10259 #[test]
10260 fn select_with_table_alias() {
10261 let s = parse("SELECT * FROM users AS u");
10262 let Statement::Select(s) = s else {
10263 panic!("expected SELECT")
10264 };
10265 let t = &s.from.as_ref().unwrap().primary;
10266 assert_eq!(t.name, "users");
10267 assert_eq!(t.alias.as_deref(), Some("u"));
10268 }
10269
10270 #[test]
10271 fn select_with_where_eq() {
10272 let s = parse("SELECT a FROM t WHERE a = 1");
10273 let Statement::Select(s) = s else {
10274 panic!("expected SELECT")
10275 };
10276 let w = s.where_.unwrap();
10277 assert_eq!(
10278 w,
10279 Expr::Binary {
10280 lhs: Box::new(col("a")),
10281 op: BinOp::Eq,
10282 rhs: Box::new(lit_int(1)),
10283 }
10284 );
10285 }
10286
10287 #[test]
10288 fn arithmetic_precedence() {
10289 let s = parse("SELECT 1 + 2 * 3");
10290 let Statement::Select(s) = s else {
10291 panic!("expected SELECT")
10292 };
10293 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10294 panic!("wildcard?")
10295 };
10296 assert_eq!(
10297 expr,
10298 &Expr::Binary {
10299 lhs: Box::new(lit_int(1)),
10300 op: BinOp::Add,
10301 rhs: Box::new(Expr::Binary {
10302 lhs: Box::new(lit_int(2)),
10303 op: BinOp::Mul,
10304 rhs: Box::new(lit_int(3)),
10305 }),
10306 }
10307 );
10308 }
10309
10310 #[test]
10311 fn parentheses_override_precedence() {
10312 let s = parse("SELECT (1 + 2) * 3");
10313 let Statement::Select(s) = s else {
10314 panic!("expected SELECT")
10315 };
10316 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10317 panic!()
10318 };
10319 assert_eq!(
10320 expr,
10321 &Expr::Binary {
10322 lhs: Box::new(Expr::Binary {
10323 lhs: Box::new(lit_int(1)),
10324 op: BinOp::Add,
10325 rhs: Box::new(lit_int(2)),
10326 }),
10327 op: BinOp::Mul,
10328 rhs: Box::new(lit_int(3)),
10329 }
10330 );
10331 }
10332
10333 #[test]
10334 fn not_binds_below_comparison() {
10335 let s = parse("SELECT NOT a = 1 FROM t");
10337 let Statement::Select(s) = s else {
10338 panic!("expected SELECT")
10339 };
10340 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10341 panic!()
10342 };
10343 assert_eq!(
10344 expr,
10345 &Expr::Unary {
10346 op: UnOp::Not,
10347 expr: Box::new(Expr::Binary {
10348 lhs: Box::new(col("a")),
10349 op: BinOp::Eq,
10350 rhs: Box::new(lit_int(1)),
10351 }),
10352 }
10353 );
10354 }
10355
10356 #[test]
10357 fn unary_minus_binds_above_multiplication() {
10358 let s = parse("SELECT -a * 2 FROM t");
10360 let Statement::Select(s) = s else {
10361 panic!("expected SELECT")
10362 };
10363 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10364 panic!()
10365 };
10366 assert_eq!(
10367 expr,
10368 &Expr::Binary {
10369 lhs: Box::new(Expr::Unary {
10370 op: UnOp::Neg,
10371 expr: Box::new(col("a")),
10372 }),
10373 op: BinOp::Mul,
10374 rhs: Box::new(lit_int(2)),
10375 }
10376 );
10377 }
10378
10379 #[test]
10380 fn qualified_column() {
10381 let s = parse("SELECT t.col FROM t");
10382 let Statement::Select(s) = s else {
10383 panic!("expected SELECT")
10384 };
10385 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10386 panic!()
10387 };
10388 assert_eq!(
10389 expr,
10390 &Expr::Column(ColumnName {
10391 qualifier: Some("t".into()),
10392 name: "col".into()
10393 })
10394 );
10395 }
10396
10397 #[test]
10398 fn select_item_alias_with_as() {
10399 let s = parse("SELECT a AS y FROM t");
10400 let Statement::Select(s) = s else {
10401 panic!("expected SELECT")
10402 };
10403 let SelectItem::Expr { alias, .. } = &s.items[0] else {
10404 panic!()
10405 };
10406 assert_eq!(alias.as_deref(), Some("y"));
10407 }
10408
10409 #[test]
10410 fn trailing_semicolon_accepted() {
10411 let s = parse("SELECT 1;");
10412 let Statement::Select(s) = s else {
10413 panic!("expected SELECT")
10414 };
10415 assert_eq!(s.items.len(), 1);
10416 }
10417
10418 #[test]
10419 fn boolean_chain_with_and_or_not() {
10420 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
10422 let Statement::Select(s) = s else {
10423 panic!("expected SELECT")
10424 };
10425 let SelectItem::Expr { expr, .. } = &s.items[0] else {
10426 panic!()
10427 };
10428 let expected = Expr::Binary {
10429 lhs: Box::new(Expr::Unary {
10430 op: UnOp::Not,
10431 expr: Box::new(col("a")),
10432 }),
10433 op: BinOp::Or,
10434 rhs: Box::new(Expr::Binary {
10435 lhs: Box::new(col("b")),
10436 op: BinOp::And,
10437 rhs: Box::new(Expr::Unary {
10438 op: UnOp::Not,
10439 expr: Box::new(col("c")),
10440 }),
10441 }),
10442 };
10443 assert_eq!(expr, &expected);
10444 }
10445
10446 #[test]
10447 fn empty_input_errors() {
10448 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
10455 assert!(matches!(
10456 parse_statement(" \n\t ").unwrap(),
10457 Statement::Empty
10458 ));
10459 assert!(parse_statement("SELECT FROM WHERE").is_err());
10461 }
10462
10463 #[test]
10464 fn unmatched_paren_errors() {
10465 assert!(parse_statement("SELECT (1 + 2").is_err());
10466 }
10467
10468 #[test]
10469 fn display_round_trip_simple_select() {
10470 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
10471 let text = original.to_string();
10472 let again = parse_statement(&text).expect("re-parse");
10473 assert_eq!(original, again);
10474 }
10475
10476 #[test]
10479 fn create_table_single_column() {
10480 let s = parse("CREATE TABLE foo (a INT)");
10481 let Statement::CreateTable(c) = s else {
10482 panic!("expected CreateTable")
10483 };
10484 assert_eq!(c.name, "foo");
10485 assert_eq!(c.columns.len(), 1);
10486 assert_eq!(c.columns[0].name, "a");
10487 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10488 assert!(c.columns[0].nullable);
10489 }
10490
10491 #[test]
10492 fn create_table_multi_column_with_not_null_mix() {
10493 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
10494 let Statement::CreateTable(c) = s else {
10495 panic!()
10496 };
10497 assert_eq!(c.columns.len(), 4);
10498 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
10499 assert!(!c.columns[0].nullable);
10500 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
10501 assert!(c.columns[1].nullable);
10502 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
10503 assert!(!c.columns[2].nullable);
10504 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
10505 }
10506
10507 #[test]
10508 fn create_table_bigint_supported() {
10509 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
10510 let Statement::CreateTable(c) = s else {
10511 panic!()
10512 };
10513 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
10514 }
10515
10516 #[test]
10517 fn create_table_vector_default_is_f32() {
10518 let s = parse("CREATE TABLE t (v VECTOR(128))");
10519 let Statement::CreateTable(c) = s else {
10520 panic!()
10521 };
10522 assert_eq!(
10523 c.columns[0].ty,
10524 ColumnTypeName::Vector {
10525 dim: 128,
10526 encoding: VecEncoding::F32,
10527 },
10528 );
10529 }
10530
10531 #[test]
10532 fn create_table_vector_using_sq8() {
10533 for sql in [
10536 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
10537 "CREATE TABLE t (v VECTOR(128) using sq8)",
10538 ] {
10539 let s = parse(sql);
10540 let Statement::CreateTable(c) = s else {
10541 panic!()
10542 };
10543 assert_eq!(
10544 c.columns[0].ty,
10545 ColumnTypeName::Vector {
10546 dim: 128,
10547 encoding: VecEncoding::Sq8,
10548 },
10549 "{sql}",
10550 );
10551 }
10552 }
10553
10554 #[test]
10555 fn create_table_vector_using_unknown_errors() {
10556 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
10565 assert!(
10566 err.message.contains("USING")
10567 || err.message.contains("using")
10568 || err.message.contains("')'")
10569 || err.message.contains("','"),
10570 "expected USING/column-list rejection, got: {}",
10571 err.message
10572 );
10573 }
10574
10575 #[test]
10576 fn vector_using_sq8_display_roundtrips() {
10577 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
10580 let Statement::CreateTable(c) = s else {
10581 panic!()
10582 };
10583 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
10584 }
10585
10586 #[test]
10587 fn parser_recognises_placeholders() {
10588 use crate::ast::{Expr, SelectItem, Statement};
10589 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
10591 let Statement::Select(sel) = s else { panic!() };
10592 assert!(matches!(
10593 sel.items[0],
10594 SelectItem::Expr {
10595 expr: Expr::Placeholder(1),
10596 alias: None
10597 }
10598 ));
10599 let SelectItem::Expr {
10601 expr: Expr::Binary { lhs, rhs, .. },
10602 ..
10603 } = &sel.items[1]
10604 else {
10605 panic!()
10606 };
10607 assert!(matches!(**lhs, Expr::Placeholder(2)));
10608 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
10609 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
10611 panic!()
10612 };
10613 assert!(matches!(**rhs, Expr::Placeholder(3)));
10614 }
10615
10616 #[test]
10617 fn parser_rejects_dollar_zero() {
10618 assert!(parse_statement("SELECT $0").is_err());
10620 }
10621
10622 #[test]
10623 fn placeholder_display_roundtrips() {
10624 let s = parse("SELECT $42 FROM t");
10627 let printed = s.to_string();
10628 assert!(printed.contains("$42"));
10629 let again = parse(&printed);
10630 assert_eq!(s, again);
10631 }
10632
10633 #[test]
10634 fn alter_index_rebuild_bare() {
10635 use crate::ast::{AlterIndexTarget, Statement};
10636 let s = parse("ALTER INDEX my_idx REBUILD");
10637 let Statement::AlterIndex(a) = s else {
10638 panic!("expected AlterIndex, got {s:?}")
10639 };
10640 assert_eq!(a.name, "my_idx");
10641 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
10642 }
10643
10644 #[test]
10645 fn alter_index_rebuild_with_encoding() {
10646 use crate::ast::{AlterIndexTarget, Statement};
10647 for (sql, want) in [
10648 (
10649 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
10650 VecEncoding::F32,
10651 ),
10652 (
10653 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
10654 VecEncoding::Sq8,
10655 ),
10656 (
10657 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10658 VecEncoding::F16,
10659 ),
10660 ] {
10661 let s = parse(sql);
10662 let Statement::AlterIndex(a) = s else {
10663 panic!("{sql}: expected AlterIndex")
10664 };
10665 assert_eq!(a.name, "my_idx");
10666 assert_eq!(
10667 a.target,
10668 AlterIndexTarget::Rebuild {
10669 encoding: Some(want)
10670 },
10671 "{sql}"
10672 );
10673 }
10674 }
10675
10676 #[test]
10677 fn alter_index_rebuild_unknown_encoding_errors() {
10678 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
10679 assert!(
10680 err.message.contains("unknown vector encoding"),
10681 "got: {}",
10682 err.message
10683 );
10684 }
10685
10686 #[test]
10687 fn alter_index_rebuild_display_roundtrips() {
10688 for (input, want) in [
10689 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
10690 (
10691 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
10692 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
10693 ),
10694 (
10695 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10696 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
10697 ),
10698 ] {
10699 let s = parse(input);
10700 assert_eq!(s.to_string(), want);
10701 }
10702 }
10703
10704 #[test]
10705 fn create_table_unknown_type_defers_to_engine() {
10706 let stmt = parse_statement("CREATE TABLE x (a my_user_type)").unwrap();
10715 let Statement::CreateTable(t) = stmt else {
10716 panic!("expected CreateTable");
10717 };
10718 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("my_user_type"));
10719 }
10720
10721 #[test]
10722 fn create_table_missing_table_keyword_errors() {
10723 assert!(parse_statement("CREATE x (a INT)").is_err());
10724 }
10725
10726 #[test]
10730 fn parse_create_table_partition_by_range() {
10731 use crate::ast::{PartitionBySpec, PartitionKindAst};
10732 let stmt = parse_statement(
10733 "CREATE TABLE events_partitioned (id BIGINT NOT NULL, ts TIMESTAMPTZ NOT NULL, \
10734 payload JSONB) PARTITION BY RANGE (ts)",
10735 )
10736 .unwrap();
10737 let Statement::CreateTable(t) = stmt else {
10738 panic!("expected CreateTable");
10739 };
10740 assert!(t.partition_of.is_none(), "parent has no partition_of");
10741 assert_eq!(t.columns.len(), 3);
10742 let by = t.partition_by.as_ref().expect("expected PARTITION BY");
10743 assert_eq!(
10744 by,
10745 &PartitionBySpec {
10746 kind: PartitionKindAst::Range,
10747 key_columns: alloc::vec!["ts".to_string()],
10748 }
10749 );
10750 assert!(
10754 t.to_string().contains("PARTITION BY RANGE (ts)"),
10755 "Display lost PARTITION BY suffix: {t}"
10756 );
10757 }
10758
10759 #[test]
10760 fn parse_create_table_partition_of_range() {
10761 use crate::ast::{PartitionOfBoundsAst, PartitionOfSpec};
10762 let stmt = parse_statement(
10763 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned \
10764 FOR VALUES FROM ('2026-06-01 00:00:00+00') TO ('2026-07-01 00:00:00+00')",
10765 )
10766 .unwrap();
10767 let Statement::CreateTable(t) = stmt else {
10768 panic!("expected CreateTable");
10769 };
10770 assert!(t.columns.is_empty(), "child inherits columns from parent");
10771 assert!(t.partition_by.is_none());
10772 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
10773 assert_eq!(of.parent_name, "events_partitioned");
10774 let PartitionOfSpec { bounds, .. } = of.clone();
10775 match bounds {
10776 PartitionOfBoundsAst::Range { lower, upper } => {
10777 assert!(lower.to_string().contains("2026-06-01"));
10778 assert!(upper.to_string().contains("2026-07-01"));
10779 }
10780 PartitionOfBoundsAst::Default => panic!("expected Range, got Default"),
10781 }
10782 let s = t.to_string();
10786 assert!(
10787 s.contains("PARTITION OF events_partitioned"),
10788 "Display lost PARTITION OF: {s}"
10789 );
10790 assert!(s.contains("FOR VALUES FROM"), "Display lost FROM: {s}");
10791 assert!(s.contains(") TO ("), "Display lost TO: {s}");
10792 }
10793
10794 #[test]
10795 fn parse_create_table_partition_of_default() {
10796 use crate::ast::PartitionOfBoundsAst;
10797 let stmt =
10798 parse_statement("CREATE TABLE events_default PARTITION OF events_partitioned DEFAULT")
10799 .unwrap();
10800 let Statement::CreateTable(t) = stmt else {
10801 panic!("expected CreateTable");
10802 };
10803 let of = t.partition_of.as_ref().expect("expected PARTITION OF");
10804 assert_eq!(of.parent_name, "events_partitioned");
10805 assert!(matches!(of.bounds, PartitionOfBoundsAst::Default));
10806 assert!(
10807 t.to_string()
10808 .contains("PARTITION OF events_partitioned DEFAULT"),
10809 "Display lost DEFAULT: {t}"
10810 );
10811 }
10812
10813 #[test]
10814 fn parse_create_table_partition_of_rejects_columns() {
10815 let err = parse_statement(
10819 "CREATE TABLE events_2026_06 PARTITION OF events_partitioned (id BIGINT) \
10820 FOR VALUES FROM ('a') TO ('b')",
10821 );
10822 assert!(err.is_err(), "expected parse error for explicit columns");
10823 let msg = format!("{}", err.unwrap_err());
10824 assert!(
10825 msg.contains("PARTITION OF") && msg.contains("column"),
10826 "error should mention PARTITION OF + columns: {msg}"
10827 );
10828 }
10829
10830 #[test]
10831 fn insert_single_value() {
10832 let s = parse("INSERT INTO foo VALUES (42)");
10833 let Statement::Insert(i) = s else {
10834 panic!("expected Insert")
10835 };
10836 assert_eq!(i.table, "foo");
10837 assert_eq!(i.rows.len(), 1);
10838 assert_eq!(i.rows[0].len(), 1);
10839 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
10840 }
10841
10842 #[test]
10843 fn insert_multi_value_with_mixed_literals() {
10844 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
10845 let Statement::Insert(i) = s else { panic!() };
10846 assert_eq!(i.rows.len(), 1);
10847 assert_eq!(i.rows[0].len(), 5);
10848 }
10849
10850 #[test]
10851 fn insert_missing_into_errors() {
10852 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
10853 }
10854
10855 #[test]
10856 fn create_table_round_trip() {
10857 let original =
10858 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
10859 let text = original.to_string();
10860 let again = parse_statement(&text).expect("re-parse");
10861 assert_eq!(original, again);
10862 }
10863
10864 #[test]
10865 fn insert_round_trip_with_negation_and_string() {
10866 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
10867 let text = original.to_string();
10868 let again = parse_statement(&text).expect("re-parse");
10869 assert_eq!(original, again);
10870 }
10871
10872 #[test]
10873 fn unknown_keyword_at_statement_start_errors() {
10874 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
10877 assert!(err.message.contains("expected SELECT"));
10878 }
10879
10880 #[test]
10883 fn create_index_basic() {
10884 let s = parse("CREATE INDEX idx_id ON users (id)");
10885 let Statement::CreateIndex(c) = s else {
10886 panic!("expected CreateIndex")
10887 };
10888 assert_eq!(c.name, "idx_id");
10889 assert_eq!(c.table, "users");
10890 assert_eq!(c.column, "id");
10891 }
10892
10893 #[test]
10894 fn create_index_missing_on_errors() {
10895 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
10896 }
10897
10898 #[test]
10899 fn create_index_missing_paren_errors() {
10900 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
10901 }
10902
10903 #[test]
10904 fn create_index_round_trip() {
10905 let original = parse("CREATE INDEX by_name ON users (name)");
10906 let again = parse_statement(&original.to_string()).unwrap();
10907 assert_eq!(original, again);
10908 }
10909
10910 #[test]
10913 fn create_unique_index_basic() {
10914 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
10915 let Statement::CreateIndex(c) = s else {
10916 panic!("expected CreateIndex");
10917 };
10918 assert!(c.is_unique);
10919 assert_eq!(c.column, "a");
10920 assert!(c.partial_predicate.is_none());
10921 }
10922
10923 #[test]
10924 fn create_unique_index_partial() {
10925 let s = parse(
10927 "CREATE UNIQUE INDEX idx_email_templates_user_default \
10928 ON email_templates (user_address) WHERE is_default = true",
10929 );
10930 let Statement::CreateIndex(c) = s else {
10931 panic!("expected CreateIndex");
10932 };
10933 assert!(c.is_unique);
10934 assert_eq!(c.table, "email_templates");
10935 assert_eq!(c.column, "user_address");
10936 assert!(c.partial_predicate.is_some());
10937 }
10938
10939 #[test]
10940 fn create_unique_index_composite_with_predicate() {
10941 let s = parse(
10943 "CREATE UNIQUE INDEX uq_calendar_events_instance \
10944 ON calendar_events (calendar_id, uid, recurrence_id) \
10945 WHERE recurrence_id IS NOT NULL",
10946 );
10947 let Statement::CreateIndex(c) = s else {
10948 panic!("expected CreateIndex");
10949 };
10950 assert!(c.is_unique);
10951 assert_eq!(c.column, "calendar_id");
10952 assert_eq!(
10953 c.extra_columns,
10954 vec!["uid".to_string(), "recurrence_id".to_string()]
10955 );
10956 assert!(c.partial_predicate.is_some());
10957 }
10958
10959 #[test]
10960 fn create_unique_index_using_btree_ok() {
10961 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
10962 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
10963 }
10964
10965 #[test]
10966 fn create_unique_index_using_hnsw_rejected() {
10967 let err =
10968 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
10969 assert!(err.message.contains("UNIQUE"), "{}", err.message);
10970 }
10971
10972 #[test]
10973 fn create_unique_index_round_trip() {
10974 let original = parse(
10975 "CREATE UNIQUE INDEX uq_calendar_events_master \
10976 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
10977 );
10978 let again = parse_statement(&original.to_string()).unwrap();
10979 assert_eq!(original, again);
10980 }
10981
10982 #[test]
10983 fn create_unique_without_index_errors() {
10984 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
10985 assert!(err.message.contains("INDEX"), "{}", err.message);
10986 }
10987
10988 #[test]
10991 fn create_table_bytea_column() {
10992 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
10993 let Statement::CreateTable(c) = s else {
10994 panic!("expected CreateTable");
10995 };
10996 assert_eq!(c.columns.len(), 2);
10997 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
10998 assert!(!c.columns[1].nullable);
10999 }
11000
11001 #[test]
11002 fn create_table_bytes_alias_column() {
11003 let s = parse("CREATE TABLE t (blob BYTES)");
11004 let Statement::CreateTable(c) = s else {
11005 panic!("expected CreateTable");
11006 };
11007 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
11008 }
11009
11010 #[test]
11011 fn bytea_round_trip_display() {
11012 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
11013 let again = parse_statement(&original.to_string()).unwrap();
11014 assert_eq!(original, again);
11015 }
11016
11017 #[test]
11020 fn begin_commit_rollback_parse_as_unit_variants() {
11021 assert_eq!(parse("BEGIN"), Statement::Begin);
11022 assert_eq!(parse("COMMIT"), Statement::Commit);
11023 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
11024 assert_eq!(parse("BEGIN;"), Statement::Begin);
11026 }
11027
11028 #[test]
11031 fn inner_product_binop_parses() {
11032 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
11033 let Statement::Select(s) = s else { panic!() };
11034 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11035 panic!()
11036 };
11037 assert!(matches!(
11038 expr,
11039 Expr::Binary {
11040 op: BinOp::InnerProduct,
11041 ..
11042 }
11043 ));
11044 }
11045
11046 #[test]
11047 fn cosine_distance_binop_parses() {
11048 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
11049 let Statement::Select(s) = s else { panic!() };
11050 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11051 panic!()
11052 };
11053 assert!(matches!(
11054 expr,
11055 Expr::Binary {
11056 op: BinOp::CosineDistance,
11057 ..
11058 }
11059 ));
11060 }
11061
11062 #[test]
11063 fn vector_cast_postfix_wraps_string_literal() {
11064 let s = parse("SELECT '[1,2,3]'::vector FROM t");
11065 let Statement::Select(s) = s else { panic!() };
11066 let SelectItem::Expr { expr, .. } = &s.items[0] else {
11067 panic!()
11068 };
11069 assert!(matches!(
11070 expr,
11071 Expr::Cast {
11072 target: CastTarget::Vector,
11073 ..
11074 }
11075 ));
11076 }
11077
11078 #[test]
11079 fn unsupported_cast_target_errors() {
11080 let err = parse_statement("SELECT 1::, FROM t").unwrap_err();
11087 assert!(err.message.contains("expected type ident after `::`"));
11088 }
11089
11090 #[test]
11091 fn tx_statements_round_trip() {
11092 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
11093 let original = parse(q);
11094 let again = parse_statement(&original.to_string()).unwrap();
11095 assert_eq!(original, again);
11096 }
11097 }
11098
11099 #[test]
11100 fn interval_text_parsing_units() {
11101 assert_eq!(parse_interval_text("1 day"), Some((0, 1, 0)));
11105 assert_eq!(
11106 parse_interval_text("24 hours"),
11107 Some((0, 0, 86_400_000_000))
11108 );
11109 assert_eq!(parse_interval_text("1 second"), Some((0, 0, 1_000_000)));
11110 assert_eq!(parse_interval_text("1 month"), Some((1, 0, 0)));
11111 assert_eq!(parse_interval_text("2 years"), Some((24, 0, 0)));
11112 assert_eq!(parse_interval_text("1 week"), Some((0, 7, 0)));
11113 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0, 0)));
11115 assert_eq!(
11116 parse_interval_text("1 day 2 hours"),
11117 Some((0, 1, 7_200_000_000))
11118 );
11119 assert_eq!(parse_interval_text("-1 day"), Some((0, -1, 0)));
11121 assert_eq!(parse_interval_text(""), None);
11123 assert_eq!(parse_interval_text("garbage"), None);
11124 assert_eq!(parse_interval_text("1 fortnight"), None);
11125 assert_eq!(parse_interval_text("1"), None);
11126 }
11127
11128 #[test]
11129 fn interval_literal_roundtrips_via_display() {
11130 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
11131 let s = parsed.to_string();
11132 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
11134 let again = parse_statement(&s).unwrap();
11136 assert_eq!(parsed, again);
11137 }
11138
11139 #[test]
11142 fn parser_recognises_create_publication_bare() {
11143 let s = parse("CREATE PUBLICATION pub_a");
11144 let Statement::CreatePublication(p) = s else {
11145 panic!("expected CreatePublication, got {s:?}")
11146 };
11147 assert_eq!(p.name, "pub_a");
11148 assert_eq!(p.scope, PublicationScope::AllTables);
11149 }
11150
11151 #[test]
11152 fn parser_recognises_create_publication_for_all_tables() {
11153 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
11154 let Statement::CreatePublication(p) = s else {
11155 panic!("expected CreatePublication, got {s:?}")
11156 };
11157 assert_eq!(p.name, "pub_a");
11158 assert_eq!(p.scope, PublicationScope::AllTables);
11159 }
11160
11161 #[test]
11162 fn parser_recognises_drop_publication() {
11163 let s = parse("DROP PUBLICATION pub_a");
11164 let Statement::DropPublication(name) = s else {
11165 panic!("expected DropPublication, got {s:?}")
11166 };
11167 assert_eq!(name, "pub_a");
11168 }
11169
11170 #[test]
11171 fn parser_recognises_for_table_list() {
11172 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
11173 let Statement::CreatePublication(p) = s else {
11174 panic!("expected CreatePublication, got {s:?}")
11175 };
11176 assert_eq!(p.name, "pub_a");
11177 let PublicationScope::ForTables(ts) = p.scope else {
11178 panic!("expected ForTables scope")
11179 };
11180 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
11181 }
11182
11183 #[test]
11184 fn parser_recognises_for_tables_plural() {
11185 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
11187 let Statement::CreatePublication(p) = s else {
11188 panic!("expected CreatePublication, got {s:?}")
11189 };
11190 let PublicationScope::ForTables(ts) = p.scope else {
11191 panic!("expected ForTables")
11192 };
11193 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11194 }
11195
11196 #[test]
11197 fn parser_recognises_for_all_tables_except_list() {
11198 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
11199 let Statement::CreatePublication(p) = s else {
11200 panic!()
11201 };
11202 let PublicationScope::AllTablesExcept(ts) = p.scope else {
11203 panic!("expected AllTablesExcept")
11204 };
11205 assert_eq!(ts, alloc::vec!["t1", "t2"]);
11206 }
11207
11208 #[test]
11209 fn parser_rejects_for_table_with_empty_list() {
11210 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
11212 .expect_err("must error on empty list");
11213 assert!(!err.message.is_empty());
11216 }
11217
11218 #[test]
11219 fn parser_recognises_show_publications() {
11220 let s = parse("SHOW PUBLICATIONS");
11223 assert!(matches!(s, Statement::ShowPublications));
11224 }
11225
11226 #[test]
11229 fn parser_recognises_create_subscription_single_publication() {
11230 let s = parse(
11231 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
11232 );
11233 let Statement::CreateSubscription(c) = s else {
11234 panic!("expected CreateSubscription, got {s:?}")
11235 };
11236 assert_eq!(c.name, "sub_a");
11237 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
11238 assert_eq!(c.publications, alloc::vec!["pub_a"]);
11239 }
11240
11241 #[test]
11242 fn parser_recognises_create_subscription_multi_publication() {
11243 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
11244 let Statement::CreateSubscription(c) = s else {
11245 panic!()
11246 };
11247 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
11248 }
11249
11250 #[test]
11251 fn parser_rejects_create_subscription_missing_connection() {
11252 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
11253 .expect_err("must error on missing CONNECTION");
11254 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
11255 }
11256
11257 #[test]
11258 fn parser_rejects_create_subscription_missing_publication() {
11259 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
11260 .expect_err("must error on missing PUBLICATION");
11261 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
11262 }
11263
11264 #[test]
11265 fn parser_recognises_drop_subscription() {
11266 let s = parse("DROP SUBSCRIPTION sub_a");
11267 let Statement::DropSubscription(name) = s else {
11268 panic!("expected DropSubscription, got {s:?}")
11269 };
11270 assert_eq!(name, "sub_a");
11271 }
11272
11273 #[test]
11274 fn parser_recognises_show_subscriptions() {
11275 let s = parse("SHOW SUBSCRIPTIONS");
11276 assert!(matches!(s, Statement::ShowSubscriptions));
11277 }
11278
11279 #[test]
11280 fn parser_recognises_wait_for_wal_position_no_timeout() {
11281 let s = parse("WAIT FOR WAL POSITION 12345");
11282 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11283 panic!("expected WaitForWalPosition, got {s:?}")
11284 };
11285 assert_eq!(pos, 12345);
11286 assert!(timeout_ms.is_none());
11287 }
11288
11289 #[test]
11290 fn parser_recognises_wait_for_wal_position_with_timeout() {
11291 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
11292 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
11293 panic!()
11294 };
11295 assert_eq!(pos, 67890);
11296 assert_eq!(timeout_ms, Some(5000));
11297 }
11298
11299 #[test]
11300 fn parser_rejects_wait_with_negative_position() {
11301 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
11307 assert!(!err.message.is_empty());
11308 }
11309
11310 #[test]
11311 fn parser_recognises_bare_analyze() {
11312 let s = parse("ANALYZE");
11313 assert!(matches!(s, Statement::Analyze(None)));
11314 }
11315
11316 #[test]
11317 fn parser_recognises_analyze_with_table() {
11318 let s = parse("ANALYZE users");
11319 let Statement::Analyze(Some(name)) = s else {
11320 panic!("expected Analyze, got {s:?}")
11321 };
11322 assert_eq!(name, "users");
11323 }
11324
11325 #[test]
11326 fn parser_recognises_analyze_with_quoted_table() {
11327 let s = parse("ANALYZE \"Mixed Case\"");
11328 let Statement::Analyze(Some(name)) = s else {
11329 panic!()
11330 };
11331 assert_eq!(name, "Mixed Case");
11332 }
11333
11334 #[test]
11335 fn parser_rejects_analyze_with_garbage_token() {
11336 let err = parse_statement("ANALYZE 42").expect_err("must error");
11337 assert!(!err.message.is_empty());
11338 }
11339
11340 #[test]
11341 fn analyze_display_roundtrips() {
11342 for sql in ["ANALYZE", "ANALYZE users"] {
11343 let s = parse(sql);
11344 let printed = s.to_string();
11345 let again = parse_statement(&printed)
11346 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11347 assert_eq!(s, again);
11348 }
11349 }
11350
11351 #[test]
11352 fn wait_for_display_roundtrips() {
11353 for sql in [
11354 "WAIT FOR WAL POSITION 12345",
11355 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
11356 ] {
11357 let s = parse(sql);
11358 let printed = s.to_string();
11359 let again = parse_statement(&printed)
11360 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11361 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11362 }
11363 }
11364
11365 #[test]
11366 fn subscription_ddl_display_roundtrips() {
11367 for sql in [
11368 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
11369 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
11370 "DROP SUBSCRIPTION sub_a",
11371 "SHOW SUBSCRIPTIONS",
11372 ] {
11373 let s = parse(sql);
11374 let printed = s.to_string();
11375 let again = parse_statement(&printed)
11376 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11377 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11378 }
11379 }
11380
11381 #[test]
11382 fn parser_drop_dispatches_user_vs_publication() {
11383 let s = parse("DROP USER 'alice'");
11386 let Statement::DropUser(name) = s else {
11387 panic!("expected DropUser, got {s:?}")
11388 };
11389 assert_eq!(name, "alice");
11390 let s = parse("DROP PUBLICATION p1");
11392 assert!(matches!(s, Statement::DropPublication(_)));
11393 }
11394
11395 #[test]
11396 fn publication_ddl_display_roundtrips() {
11397 for sql in [
11400 "CREATE PUBLICATION pub_a",
11401 "CREATE PUBLICATION pub_a FOR ALL TABLES",
11402 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
11403 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
11404 "DROP PUBLICATION pub_a",
11405 "SHOW PUBLICATIONS",
11406 ] {
11407 let s = parse(sql);
11408 let printed = s.to_string();
11409 let again = parse_statement(&printed)
11410 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11411 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11412 }
11413 }
11414
11415 #[test]
11418 fn create_function_returns_trigger_plpgsql_minimal() {
11419 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
11420 let s = parse(sql);
11421 let Statement::CreateFunction(f) = s else {
11422 panic!("expected CreateFunction");
11423 };
11424 assert_eq!(f.name, "noop");
11425 assert!(!f.or_replace);
11426 assert!(f.args.is_empty());
11427 assert!(matches!(f.returns, FunctionReturn::Trigger));
11428 assert_eq!(f.language, "plpgsql");
11429 let FunctionBody::PlPgSql(block) = f.body else {
11430 panic!("expected PlPgSql body");
11431 };
11432 assert_eq!(block.statements.len(), 1);
11433 assert!(matches!(
11434 block.statements[0],
11435 PlPgSqlStmt::Return(ReturnTarget::New)
11436 ));
11437 }
11438
11439 #[test]
11440 fn create_function_or_replace_with_assignment() {
11441 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
11444BEGIN
11445 NEW.search_vector := to_tsvector('english', NEW.subject);
11446 RETURN NEW;
11447END;
11448$$";
11449 let s = parse(sql);
11450 let Statement::CreateFunction(f) = s else {
11451 panic!("expected CreateFunction");
11452 };
11453 assert!(f.or_replace);
11454 let FunctionBody::PlPgSql(block) = &f.body else {
11455 panic!("expected PlPgSql body");
11456 };
11457 assert_eq!(block.statements.len(), 2);
11458 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
11460 panic!("expected Assign as first stmt");
11461 };
11462 match target {
11463 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
11464 other => panic!("expected NEW.col, got {other:?}"),
11465 }
11466 assert!(matches!(
11468 block.statements[1],
11469 PlPgSqlStmt::Return(ReturnTarget::New)
11470 ));
11471 }
11472
11473 #[test]
11474 fn create_trigger_after_insert_or_update() {
11475 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
11476 let s = parse(sql);
11477 let Statement::CreateTrigger(t) = s else {
11478 panic!("expected CreateTrigger");
11479 };
11480 assert_eq!(t.name, "tg");
11481 assert_eq!(t.table, "messages");
11482 assert_eq!(t.timing, TriggerTiming::After);
11483 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
11484 assert_eq!(t.for_each, TriggerForEach::Row);
11485 assert_eq!(t.function, "update_sv");
11486 }
11487
11488 #[test]
11489 fn create_trigger_before_delete_execute_procedure_alias() {
11490 let sql =
11492 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
11493 let s = parse(sql);
11494 let Statement::CreateTrigger(t) = s else {
11495 panic!("expected CreateTrigger");
11496 };
11497 assert_eq!(t.timing, TriggerTiming::Before);
11498 assert_eq!(t.events, vec![TriggerEvent::Delete]);
11499 }
11500
11501 #[test]
11502 fn drop_trigger_if_exists_round_trips() {
11503 let s = Statement::DropTrigger {
11508 name: "tg".into(),
11509 table: "messages".into(),
11510 if_exists: true,
11511 };
11512 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
11513 }
11514
11515 #[test]
11516 fn trigger_ddl_display_roundtrips_through_parser() {
11517 for sql in [
11521 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
11522 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
11523 ] {
11524 let s = parse(sql);
11525 let printed = s.to_string();
11526 let again = parse_statement(&printed)
11527 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
11528 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
11529 }
11530 }
11531}