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 is_vector_opclass_name(name: &str) -> bool {
81 let lc = name.to_ascii_lowercase();
82 matches!(
83 lc.as_str(),
84 "vector_cosine_ops"
85 | "vector_l2_ops"
86 | "vector_ip_ops"
87 | "halfvec_cosine_ops"
88 | "halfvec_l2_ops"
89 | "halfvec_ip_ops"
90 | "sq8_cosine_ops"
91 | "sq8_l2_ops"
92 | "sq8_ip_ops"
93 | "gin_trgm_ops"
99 | "gist_trgm_ops"
100 | "text_pattern_ops"
105 | "varchar_pattern_ops"
106 | "bpchar_pattern_ops"
107 | "int4_ops"
108 | "int8_ops"
109 | "text_ops"
110 )
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ParseError {
115 pub message: String,
116 pub token_pos: usize,
118}
119
120impl fmt::Display for ParseError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(
123 f,
124 "parse error at token #{}: {}",
125 self.token_pos, self.message
126 )
127 }
128}
129
130impl From<LexError> for ParseError {
131 fn from(e: LexError) -> Self {
132 Self {
133 message: format!("lex: {e}"),
134 token_pos: 0,
135 }
136 }
137}
138
139pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
145 let tokens = lexer::tokenize(input)?;
146 let mut p = Parser::new(tokens);
147 let expr = p.parse_expr(0)?;
148 p.expect_eof()?;
149 Ok(expr)
150}
151
152pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
155 let tokens = lexer::tokenize(input)?;
156 let mut p = Parser::new(tokens);
157 let stmt = p.parse_one_statement()?;
158 if matches!(p.peek(), Token::Semicolon) {
159 p.advance();
160 }
161 p.expect_eof()?;
162 Ok(stmt)
163}
164
165struct Parser {
166 tokens: Vec<Token>,
167 pos: usize,
168}
169
170impl Parser {
171 fn new(tokens: Vec<Token>) -> Self {
172 Self { tokens, pos: 0 }
173 }
174
175 fn peek(&self) -> &Token {
176 &self.tokens[self.pos]
178 }
179
180 fn advance(&mut self) -> Token {
181 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
182 if self.pos + 1 < self.tokens.len() {
183 self.pos += 1;
184 }
185 t
186 }
187
188 fn err(&self, message: String) -> ParseError {
189 ParseError {
190 message,
191 token_pos: self.pos,
192 }
193 }
194
195 fn expect_eof(&self) -> Result<(), ParseError> {
196 if matches!(self.peek(), Token::Eof) {
197 Ok(())
198 } else {
199 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
200 }
201 }
202
203 fn consume_until_statement_boundary(&mut self) {
208 loop {
209 match self.peek() {
210 Token::Semicolon | Token::Eof => return,
211 _ => self.advance(),
212 };
213 }
214 }
215
216 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
217 let first = match self.advance() {
218 Token::Ident(s) | Token::QuotedIdent(s) => s,
219 other => {
220 return Err(ParseError {
221 message: format!("expected identifier, got {other:?}"),
222 token_pos: self.pos.saturating_sub(1),
223 });
224 }
225 };
226 if matches!(self.peek(), Token::Dot) {
233 self.advance();
234 match self.advance() {
235 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
236 other => {
237 return Err(ParseError {
238 message: format!("expected identifier after '{first}.', got {other:?}"),
239 token_pos: self.pos.saturating_sub(1),
240 });
241 }
242 }
243 }
244 Ok(first)
245 }
246
247 #[allow(clippy::too_many_lines)]
248 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
249 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
257 return Ok(Statement::Empty);
258 }
259 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
269 let lc = s.to_ascii_lowercase();
270 if is_dump_noise_statement(&lc) {
271 self.consume_until_statement_boundary();
272 return Ok(Statement::Empty);
273 }
274 }
275 match self.peek() {
276 Token::Select => self.parse_select_stmt(),
277 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
293 self.advance();
294 let body_text = match self.advance() {
295 Token::String(s) => s,
296 other => {
297 return Err(self.err(alloc::format!(
298 "expected dollar-quoted body after DO, got {other:?}"
299 )));
300 }
301 };
302 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
304 self.advance();
305 let _ = self.expect_ident_like()?;
306 }
307 let block = parse_plpgsql_body(&body_text)?;
312 Ok(Statement::DoBlock(block))
313 }
314 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
318 self.advance();
319 self.parse_with_cte_then_select()
320 }
321 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
324 self.advance();
325 let mut analyze = false;
326 let mut suggest = false;
327 if matches!(self.peek(), Token::LParen) {
329 self.advance();
330 let opt = match self.peek().clone() {
331 Token::Ident(s) | Token::QuotedIdent(s) => s,
332 other => {
333 return Err(self.err(format!(
334 "expected option keyword inside EXPLAIN (…), got {other:?}"
335 )));
336 }
337 };
338 if !opt.eq_ignore_ascii_case("suggest") {
339 return Err(self.err(format!(
340 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
341 )));
342 }
343 self.advance();
344 if !matches!(self.peek(), Token::RParen) {
345 return Err(self.err(format!(
346 "expected ')' after EXPLAIN option, got {:?}",
347 self.peek()
348 )));
349 }
350 self.advance();
351 suggest = true;
352 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
353 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
354 {
355 self.advance();
356 analyze = true;
357 }
358 let inner = self.parse_select_stmt()?;
359 let Statement::Select(s) = inner else {
360 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
361 };
362 Ok(Statement::Explain(crate::ast::ExplainStatement {
363 analyze,
364 inner: Box::new(s),
365 suggest,
366 }))
367 }
368 Token::Create => self.parse_create_stmt(),
369 Token::Insert => self.parse_insert_stmt(),
370 Token::Begin => {
371 self.advance();
372 Ok(Statement::Begin)
373 }
374 Token::Commit => {
375 self.advance();
376 Ok(Statement::Commit)
377 }
378 Token::Rollback => {
379 self.advance();
380 if matches!(self.peek(), Token::To) {
384 self.advance();
385 if matches!(self.peek(), Token::Savepoint) {
386 self.advance();
387 }
388 let name = self.expect_ident_like()?;
389 Ok(Statement::RollbackToSavepoint(name))
390 } else {
391 Ok(Statement::Rollback)
392 }
393 }
394 Token::Savepoint => {
395 self.advance();
396 let name = self.expect_ident_like()?;
397 Ok(Statement::Savepoint(name))
398 }
399 Token::Release => {
400 self.advance();
401 if matches!(self.peek(), Token::Savepoint) {
404 self.advance();
405 }
406 let name = self.expect_ident_like()?;
407 Ok(Statement::ReleaseSavepoint(name))
408 }
409 Token::Show => {
410 self.advance();
411 let target = match self.advance() {
417 Token::Tables => "tables".to_string(),
418 Token::Create => "create".to_string(),
422 Token::Index => "index".to_string(),
425 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
426 other => {
427 return Err(self.err(format!(
428 "expected SHOW target, got {other:?}"
429 )));
430 }
431 };
432 match target.as_str() {
433 "tables" => Ok(Statement::ShowTables),
434 "users" => Ok(Statement::ShowUsers),
435 "indexes" | "index" | "keys" => {
445 if !matches!(self.peek(), Token::From) {
446 return Err(self.err(format!(
447 "expected FROM after SHOW INDEXES, got {:?}",
448 self.peek()
449 )));
450 }
451 self.advance();
452 let table = self.expect_ident_like()?;
453 Ok(Statement::ShowIndexes(table))
454 }
455 "status" => Ok(Statement::ShowStatus),
460 "variables" => Ok(Statement::ShowVariables),
461 "processlist" => Ok(Statement::ShowProcesslist),
463 "create" => {
464 let kind = match self.advance() {
467 Token::Ident(s) | Token::QuotedIdent(s) => s,
468 Token::Table => "table".to_string(),
469 other => {
470 return Err(self.err(format!(
471 "expected TABLE after SHOW CREATE, got {other:?}"
472 )));
473 }
474 };
475 if !kind.eq_ignore_ascii_case("table") {
476 return Err(self.err(format!(
477 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
478 )));
479 }
480 let name = self.expect_ident_like()?;
481 Ok(Statement::ShowCreateTable(name))
482 }
483 "databases" | "schemas" => Ok(Statement::ShowDatabases),
489 "publications" => Ok(Statement::ShowPublications),
494 "subscriptions" => Ok(Statement::ShowSubscriptions),
496 "columns" => {
497 if !matches!(self.peek(), Token::From) {
498 return Err(self.err(format!(
499 "expected FROM after SHOW COLUMNS, got {:?}",
500 self.peek()
501 )));
502 }
503 self.advance();
504 let table = self.expect_ident_like()?;
505 Ok(Statement::ShowColumns(table))
506 }
507 other => Err(self.err(format!(
508 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
509 ))),
510 }
511 }
512 Token::Drop => {
518 self.advance();
519 match self.peek() {
520 Token::Publication => {
521 self.advance();
522 let name = self.expect_ident_or_string()?;
523 Ok(Statement::DropPublication(name))
524 }
525 Token::Subscription => {
526 self.advance();
527 let name = self.expect_ident_or_string()?;
528 Ok(Statement::DropSubscription(name))
529 }
530 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
531 self.advance();
532 let name = self.expect_ident_or_string()?;
533 Ok(Statement::DropUser(name))
534 }
535 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
537 self.advance();
538 let if_exists = self.consume_if_exists();
539 let name = self.expect_ident_like()?;
540 if !matches!(self.peek(), Token::On) {
542 return Err(self.err(alloc::format!(
543 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
544 self.peek()
545 )));
546 }
547 self.advance();
548 let table = self.expect_ident_like()?;
549 Ok(Statement::DropTrigger {
550 name,
551 table,
552 if_exists,
553 })
554 }
555 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
559 self.advance();
560 let if_exists = self.consume_if_exists();
561 let name = self.expect_ident_like()?;
562 if matches!(self.peek(), Token::LParen) {
564 self.advance();
565 let mut depth = 1usize;
567 while depth > 0 {
568 match self.peek() {
569 Token::LParen => depth += 1,
570 Token::RParen => depth -= 1,
571 Token::Eof => {
572 return Err(self.err(alloc::format!(
573 "unterminated arg list in DROP FUNCTION {name:?}"
574 )));
575 }
576 _ => {}
577 }
578 self.advance();
579 }
580 }
581 Ok(Statement::DropFunction { name, if_exists })
582 }
583 Token::Table => {
591 self.advance();
592 let if_exists = self.consume_if_exists();
593 let mut names: Vec<String> = Vec::new();
594 loop {
595 names.push(self.expect_ident_like()?);
596 if matches!(self.peek(), Token::Comma) {
597 self.advance();
598 continue;
599 }
600 break;
601 }
602 if matches!(
603 self.peek(),
604 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
605 || s.eq_ignore_ascii_case("restrict")
606 ) {
607 self.advance();
608 }
609 Ok(Statement::DropTable { names, if_exists })
610 }
611 Token::Index => {
617 self.advance();
618 let if_exists = self.consume_if_exists();
619 let name = self.expect_ident_like()?;
620 if matches!(
621 self.peek(),
622 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
623 || s.eq_ignore_ascii_case("restrict")
624 ) {
625 self.advance();
626 }
627 Ok(Statement::DropIndex { name, if_exists })
628 }
629 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
635 self.advance();
636 let if_exists = self.consume_if_exists();
637 let mut names = vec![self.expect_ident_like()?];
638 while matches!(self.peek(), Token::Comma) {
639 self.advance();
640 names.push(self.expect_ident_like()?);
641 }
642 if matches!(
643 self.peek(),
644 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
645 || s.eq_ignore_ascii_case("restrict")
646 ) {
647 self.advance();
648 }
649 Ok(Statement::DropSchema { names, if_exists })
650 }
651 Token::Ident(s) | Token::QuotedIdent(s)
654 if s.eq_ignore_ascii_case("type") =>
655 {
656 self.advance();
657 let if_exists = self.consume_if_exists();
658 let mut names = vec![self.expect_ident_like()?];
659 while matches!(self.peek(), Token::Comma) {
660 self.advance();
661 names.push(self.expect_ident_like()?);
662 }
663 if matches!(
664 self.peek(),
665 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
666 || s.eq_ignore_ascii_case("restrict")
667 ) {
668 self.advance();
669 }
670 Ok(Statement::DropType { names, if_exists })
671 }
672 Token::Ident(s) | Token::QuotedIdent(s)
675 if s.eq_ignore_ascii_case("domain") =>
676 {
677 self.advance();
678 let if_exists = self.consume_if_exists();
679 let mut names = vec![self.expect_ident_like()?];
680 while matches!(self.peek(), Token::Comma) {
681 self.advance();
682 names.push(self.expect_ident_like()?);
683 }
684 if matches!(
685 self.peek(),
686 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
687 || s.eq_ignore_ascii_case("restrict")
688 ) {
689 self.advance();
690 }
691 Ok(Statement::DropDomain { names, if_exists })
692 }
693 Token::Ident(s) | Token::QuotedIdent(s)
696 if s.eq_ignore_ascii_case("materialized") =>
697 {
698 self.advance();
699 let nxt = self.peek().clone();
700 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
701 {
702 return Err(self.err(alloc::format!(
703 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
704 )));
705 }
706 self.advance();
707 let if_exists = self.consume_if_exists();
708 let mut names = vec![self.expect_ident_like()?];
709 while matches!(self.peek(), Token::Comma) {
710 self.advance();
711 names.push(self.expect_ident_like()?);
712 }
713 if matches!(
714 self.peek(),
715 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
716 || s.eq_ignore_ascii_case("restrict")
717 ) {
718 self.advance();
719 }
720 Ok(Statement::DropMaterializedView { names, if_exists })
721 }
722 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
725 self.advance();
726 let if_exists = self.consume_if_exists();
727 let mut names = vec![self.expect_ident_like()?];
728 while matches!(self.peek(), Token::Comma) {
729 self.advance();
730 names.push(self.expect_ident_like()?);
731 }
732 if matches!(
733 self.peek(),
734 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
735 || s.eq_ignore_ascii_case("restrict")
736 ) {
737 self.advance();
738 }
739 Ok(Statement::DropView { names, if_exists })
740 }
741 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
745 self.advance();
746 let if_exists = self.consume_if_exists();
747 let mut names = vec![self.expect_ident_like()?];
748 while matches!(self.peek(), Token::Comma) {
749 self.advance();
750 names.push(self.expect_ident_like()?);
751 }
752 if matches!(
753 self.peek(),
754 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
755 || s.eq_ignore_ascii_case("restrict")
756 ) {
757 self.advance();
758 }
759 Ok(Statement::DropSequence { names, if_exists })
760 }
761 other => Err(self.err(format!(
762 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
763 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
764 ))),
765 }
766 }
767 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
769 self.advance();
770 let nxt = self.peek().clone();
771 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
772 {
773 return Err(self.err(alloc::format!(
774 "expected MATERIALIZED after REFRESH, got {nxt:?}"
775 )));
776 }
777 self.advance();
778 let nxt2 = self.peek().clone();
779 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
780 {
781 return Err(self.err(alloc::format!(
782 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
783 )));
784 }
785 self.advance();
786 let name = self.expect_ident_like()?;
787 let with_data = self.parse_optional_with_data(true)?;
788 Ok(Statement::RefreshMaterializedView { name, with_data })
789 }
790 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
791 self.advance();
792 self.parse_update_after_keyword()
793 }
794 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
795 self.advance();
796 self.parse_delete_after_keyword()
797 }
798 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
802 self.advance();
803 self.parse_alter_after_keyword()
804 }
805 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
809 self.advance();
810 self.parse_wait_after_keyword()
811 }
812 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
823 self.advance();
824 let next = self.peek().clone();
825 let cold = match next {
826 Token::Ident(s) | Token::QuotedIdent(s) => s,
827 _ => {
828 return Err(
829 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
830 );
831 }
832 };
833 if !cold.eq_ignore_ascii_case("cold") {
834 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
835 }
836 self.advance();
837 let next = self.peek().clone();
838 let segments = match next {
839 Token::Ident(s) | Token::QuotedIdent(s) => s,
840 _ => {
841 return Err(self.err(format!(
842 "expected SEGMENTS after COMPACT COLD, got {:?}",
843 self.peek()
844 )));
845 }
846 };
847 if !segments.eq_ignore_ascii_case("segments") {
848 return Err(self.err(format!(
849 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
850 )));
851 }
852 self.advance();
853 Ok(Statement::CompactColdSegments)
854 }
855 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
862 self.advance();
863 self.parse_merge_after_keyword()
864 }
865 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
866 self.advance();
867 let target = match self.peek() {
868 Token::Eof | Token::Semicolon => None,
869 Token::Ident(_) | Token::QuotedIdent(_) => {
870 Some(self.expect_ident_like()?)
871 }
872 other => {
873 return Err(self.err(format!(
874 "expected table name or end of statement after ANALYZE, got {other:?}"
875 )));
876 }
877 };
878 Ok(Statement::Analyze(target))
879 }
880 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
886 self.advance();
887 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"))
892 {
893 self.advance();
894 }
895 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
900 {
901 self.advance();
902 if matches!(
904 self.peek(),
905 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
906 ) {
907 self.advance();
908 }
909 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
911 {
912 self.advance();
913 if matches!(
914 self.peek(),
915 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
916 ) {
917 self.advance();
918 }
919 }
920 return Ok(Statement::Empty);
921 }
922 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
932 {
933 self.advance(); match self.peek().clone() {
935 Token::Default => {
936 self.advance();
937 }
938 Token::String(_)
939 | Token::Ident(_)
940 | Token::QuotedIdent(_) => {
941 self.advance();
942 }
943 other => {
944 return Err(self.err(alloc::format!(
945 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
946 )));
947 }
948 }
949 return Ok(Statement::Empty);
950 }
951 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
954 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
955 {
956 self.advance(); self.advance(); if matches!(
959 self.peek(),
960 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
961 ) {
962 self.advance();
963 }
964 return Ok(Statement::Empty);
965 }
966 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
971 loop {
972 let lhs = match self.peek().clone() {
973 Token::SessionVar(s) => {
974 self.advance();
975 s
976 }
977 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
978 other => {
979 return Err(self.err(format!(
980 "expected parameter name after SET, got {other:?}"
981 )));
982 }
983 };
984 match self.peek() {
986 Token::Eq => {
987 self.advance();
988 }
989 Token::To => {
990 self.advance();
991 }
992 other => {
993 return Err(self.err(format!(
994 "expected `=` or TO after SET {lhs}, got {other:?}"
995 )));
996 }
997 }
998 let value = self.parse_set_value()?;
999 pairs.push((lhs, value));
1000 if matches!(self.peek(), Token::Comma) {
1001 self.advance();
1002 continue;
1003 }
1004 break;
1005 }
1006 if pairs.len() == 1 {
1007 let (name, value) = pairs.into_iter().next().unwrap();
1008 Ok(Statement::SetParameter { name, value })
1009 } else {
1010 Ok(Statement::SetParameterList(pairs))
1011 }
1012 }
1013 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1015 self.advance();
1016 match self.peek().clone() {
1017 Token::All => {
1018 self.advance();
1019 Ok(Statement::ResetParameter(None))
1020 }
1021 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1022 self.advance();
1023 Ok(Statement::ResetParameter(None))
1024 }
1025 _ => {
1026 let name = self.parse_set_param_name()?;
1027 Ok(Statement::ResetParameter(Some(name)))
1028 }
1029 }
1030 }
1031 other => Err(self.err(format!(
1032 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1033 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1034 ))),
1035 }
1036 }
1037
1038 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1039 debug_assert!(matches!(self.peek(), Token::Create));
1040 self.advance();
1041 match self.peek() {
1042 Token::Table => self.parse_create_table_stmt_after_create(),
1043 Token::Index => self.parse_create_index_stmt_after_create(false),
1044 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1051 self.advance();
1052 if !matches!(self.peek(), Token::Index) {
1053 return Err(self.err(alloc::format!(
1054 "expected INDEX after CREATE UNIQUE, got {:?}",
1055 self.peek()
1056 )));
1057 }
1058 self.parse_create_index_stmt_after_create(true)
1059 }
1060 Token::Publication => {
1061 self.advance();
1062 self.parse_create_publication_after_keyword()
1063 }
1064 Token::Subscription => {
1065 self.advance();
1066 self.parse_create_subscription_after_keyword()
1067 }
1068 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1072 self.advance();
1073 self.parse_create_user_after_keyword()
1074 }
1075 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1079 self.advance();
1080 self.parse_create_extension_after_keyword()
1081 }
1082 Token::Or => {
1088 self.advance();
1089 let next = self.peek();
1090 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1091 return Err(self.err(alloc::format!(
1092 "expected REPLACE after CREATE OR, got {next:?}"
1093 )));
1094 };
1095 if !s2.eq_ignore_ascii_case("replace") {
1096 return Err(self.err(alloc::format!(
1097 "expected REPLACE after CREATE OR, got {s2:?}"
1098 )));
1099 }
1100 self.advance();
1101 self.parse_create_function_or_trigger_after_or_replace(true)
1102 }
1103 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1104 self.advance();
1105 self.parse_create_function_after_keyword(false)
1106 }
1107 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1108 self.advance();
1109 self.parse_create_trigger_after_keyword(false)
1110 }
1111 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1113 self.advance();
1114 self.parse_create_sequence_after_keyword(false)
1115 }
1116 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1118 self.advance();
1119 self.parse_create_view_after_keyword(false, false, false)
1120 }
1121 Token::Ident(s) | Token::QuotedIdent(s)
1135 if s.eq_ignore_ascii_case("algorithm")
1136 || s.eq_ignore_ascii_case("definer")
1137 || s.eq_ignore_ascii_case("sql") =>
1138 {
1139 self.consume_mysql_view_prefix()?;
1140 let next = self.peek().clone();
1145 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1146 if s2.eq_ignore_ascii_case("view"))
1147 {
1148 self.advance();
1149 self.parse_create_view_after_keyword(false, false, false)
1150 } else {
1151 Err(self.err(alloc::format!(
1152 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1153 )))
1154 }
1155 }
1156 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1158 self.advance();
1159 self.parse_create_type_after_keyword()
1160 }
1161 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1164 self.advance();
1165 self.parse_create_domain_after_keyword()
1166 }
1167 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1171 self.advance();
1172 let if_not_exists = self.parse_if_not_exists();
1173 let name = self.expect_ident_like()?;
1174 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1177 if s.eq_ignore_ascii_case("authorization"))
1178 {
1179 self.advance();
1180 let _ = self.expect_ident_like()?;
1181 }
1182 Ok(Statement::CreateSchema { name, if_not_exists })
1183 }
1184 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1186 self.advance();
1187 let next = self.peek().clone();
1188 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1189 {
1190 self.advance();
1191 self.parse_create_materialized_view_after_keyword()
1192 } else {
1193 Err(self.err(alloc::format!(
1194 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1195 )))
1196 }
1197 }
1198 Token::Ident(s) | Token::QuotedIdent(s)
1199 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1200 {
1201 self.advance();
1202 let next = self.peek().clone();
1204 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1205 {
1206 self.advance();
1207 self.parse_create_sequence_after_keyword(true)
1208 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1209 {
1210 self.advance();
1211 self.parse_create_view_after_keyword(false, false, true)
1212 } else {
1213 self.consume_until_statement_boundary();
1215 Ok(Statement::Empty)
1216 }
1217 }
1218 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1228 self.consume_mysql_routine_body();
1229 Ok(Statement::Empty)
1230 }
1231 Token::Ident(s) | Token::QuotedIdent(s)
1242 if matches!(
1243 s.to_ascii_lowercase().as_str(),
1244 "database"
1245 | "role"
1246 | "policy"
1247 | "operator"
1248 | "cast"
1249 | "rule"
1250 | "aggregate"
1251 | "language"
1252 | "collation"
1253 | "conversion"
1254 | "statistics"
1262 | "event"
1263 | "foreign"
1264 ) =>
1265 {
1266 self.consume_until_statement_boundary();
1267 Ok(Statement::Empty)
1268 }
1269 other => Err(self.err(format!(
1270 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1271 ))),
1272 }
1273 }
1274
1275 fn parse_create_function_or_trigger_after_or_replace(
1280 &mut self,
1281 or_replace: bool,
1282 ) -> Result<Statement, ParseError> {
1283 let tok = self.peek();
1284 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1285 return Err(self.err(alloc::format!(
1286 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1287 )));
1288 };
1289 if s.eq_ignore_ascii_case("function") {
1290 self.advance();
1291 self.parse_create_function_after_keyword(or_replace)
1292 } else if s.eq_ignore_ascii_case("trigger") {
1293 self.advance();
1294 self.parse_create_trigger_after_keyword(or_replace)
1295 } else if s.eq_ignore_ascii_case("view") {
1296 self.advance();
1298 self.parse_create_view_after_keyword(or_replace, false, false)
1299 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1300 self.advance();
1302 let nxt = self.peek().clone();
1303 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1304 {
1305 self.advance();
1306 self.parse_create_view_after_keyword(or_replace, false, true)
1307 } else {
1308 Err(self.err(alloc::format!(
1309 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1310 )))
1311 }
1312 } else {
1313 Err(self.err(alloc::format!(
1314 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1315 )))
1316 }
1317 }
1318
1319 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1324 self.consume_if_not_exists();
1326 let name = self.expect_ident_like()?;
1327 loop {
1330 match self.peek() {
1331 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1332 self.advance();
1333 continue;
1334 }
1335 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1336 self.advance();
1337 let _ = self.expect_ident_like()?;
1338 continue;
1339 }
1340 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1341 self.advance();
1342 let _ = self.advance();
1344 continue;
1345 }
1346 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1347 self.advance();
1348 let _ = self.advance();
1349 continue;
1350 }
1351 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1352 self.advance();
1353 continue;
1354 }
1355 _ => break,
1356 }
1357 }
1358 Ok(Statement::CreateExtension(name))
1359 }
1360
1361 fn parse_create_function_after_keyword(
1373 &mut self,
1374 or_replace: bool,
1375 ) -> Result<Statement, ParseError> {
1376 let name = self.expect_ident_like()?;
1377 if !matches!(self.peek(), Token::LParen) {
1381 return Err(self.err(alloc::format!(
1382 "expected '(' after function name {name:?}, got {:?}",
1383 self.peek()
1384 )));
1385 }
1386 self.advance();
1387 let args = self.parse_function_arg_list()?;
1388 let tok = self.peek();
1390 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1391 return Err(self.err(alloc::format!(
1392 "expected RETURNS after function arg list, got {tok:?}"
1393 )));
1394 };
1395 if !s.eq_ignore_ascii_case("returns") {
1396 return Err(self.err(alloc::format!(
1397 "expected RETURNS after function arg list, got {s:?}"
1398 )));
1399 }
1400 self.advance();
1401 let returns = self.parse_function_return()?;
1402 let mut language: Option<String> = self.parse_optional_language()?;
1405 if !matches!(self.peek(), Token::As) {
1409 return Err(self.err(alloc::format!(
1410 "expected AS before function body, got {:?}",
1411 self.peek()
1412 )));
1413 }
1414 self.advance();
1415 let body_text = match self.peek() {
1416 Token::String(s) => {
1417 let body = s.clone();
1418 self.advance();
1419 body
1420 }
1421 other => {
1422 return Err(self.err(alloc::format!(
1423 "expected $$-quoted function body after AS, got {other:?}"
1424 )));
1425 }
1426 };
1427 if language.is_none() {
1429 language = self.parse_optional_language()?;
1430 }
1431 let language = language.unwrap_or_else(|| String::from("sql"));
1432 let body = if language.eq_ignore_ascii_case("plpgsql") {
1437 match parse_plpgsql_body(&body_text) {
1438 Ok(block) => FunctionBody::PlPgSql(block),
1439 Err(_) => FunctionBody::Raw(body_text),
1445 }
1446 } else {
1447 FunctionBody::Raw(body_text)
1448 };
1449 Ok(Statement::CreateFunction(CreateFunctionStatement {
1450 name,
1451 or_replace,
1452 args,
1453 returns,
1454 language,
1455 body,
1456 }))
1457 }
1458
1459 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1463 let mut args: Vec<FunctionArg> = Vec::new();
1464 if matches!(self.peek(), Token::RParen) {
1465 self.advance();
1466 return Ok(args);
1467 }
1468 loop {
1469 let mode = if matches!(self.peek(), Token::In) {
1472 self.advance();
1473 FunctionArgMode::In
1474 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1475 {
1476 self.advance();
1477 FunctionArgMode::Out
1478 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1479 {
1480 self.advance();
1481 FunctionArgMode::InOut
1482 } else {
1483 FunctionArgMode::In
1484 };
1485 let (name, ty_token) = {
1491 let first = self.expect_ident_like()?;
1492 match self.peek() {
1495 Token::Ident(_) | Token::QuotedIdent(_) => {
1496 let ty = self.expect_ident_like()?;
1497 (Some(first), ty)
1498 }
1499 _ => (None, first),
1500 }
1501 };
1502 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1504 Some(t) => FunctionArgType::Typed(t),
1505 None => FunctionArgType::Raw(ty_token),
1506 };
1507 args.push(FunctionArg { mode, name, ty });
1508 match self.peek() {
1509 Token::Comma => {
1510 self.advance();
1511 continue;
1512 }
1513 Token::RParen => {
1514 self.advance();
1515 return Ok(args);
1516 }
1517 other => {
1518 return Err(self.err(alloc::format!(
1519 "expected , or ) in function arg list, got {other:?}"
1520 )));
1521 }
1522 }
1523 }
1524 }
1525
1526 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1527 let ident = self.expect_ident_like()?;
1528 if ident.eq_ignore_ascii_case("trigger") {
1529 return Ok(FunctionReturn::Trigger);
1530 }
1531 if ident.eq_ignore_ascii_case("void") {
1532 return Ok(FunctionReturn::Void);
1533 }
1534 match map_type_ident_to_column_type_name(&ident) {
1535 Some(t) => Ok(FunctionReturn::Type(t)),
1536 None => Ok(FunctionReturn::Other(ident)),
1537 }
1538 }
1539
1540 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1541 match self.peek() {
1542 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1543 self.advance();
1544 let lang = self.expect_ident_like()?;
1545 Ok(Some(lang.to_ascii_lowercase()))
1546 }
1547 _ => Ok(None),
1548 }
1549 }
1550
1551 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1557 let name = self.expect_ident_like()?;
1558 if matches!(self.peek(), Token::As) {
1560 self.advance();
1561 }
1562 let base_type = self.parse_column_type_name()?;
1563 let mut default: Option<Expr> = None;
1564 let mut not_null = false;
1565 let mut checks: Vec<Expr> = Vec::new();
1566 loop {
1567 match self.peek() {
1568 Token::Default => {
1569 if default.is_some() {
1570 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1571 }
1572 self.advance();
1573 default = Some(self.parse_expr(0)?);
1574 }
1575 Token::Not => {
1576 self.advance();
1577 if !matches!(self.peek(), Token::Null) {
1578 return Err(self.err(alloc::format!(
1579 "expected NULL after NOT in DOMAIN, got {:?}",
1580 self.peek()
1581 )));
1582 }
1583 self.advance();
1584 not_null = true;
1585 }
1586 Token::Null => {
1587 self.advance();
1588 }
1592 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1593 self.advance();
1594 if !matches!(self.peek(), Token::LParen) {
1595 return Err(self.err(alloc::format!(
1596 "expected '(' after CHECK in DOMAIN, got {:?}",
1597 self.peek()
1598 )));
1599 }
1600 self.advance();
1601 let expr = self.parse_expr(0)?;
1602 if !matches!(self.peek(), Token::RParen) {
1603 return Err(self.err(alloc::format!(
1604 "expected ')' after CHECK expr, got {:?}",
1605 self.peek()
1606 )));
1607 }
1608 self.advance();
1609 checks.push(expr);
1610 }
1611 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1615 self.advance();
1616 let _ = self.expect_ident_like()?;
1617 }
1618 _ => break,
1619 }
1620 }
1621 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1622 name,
1623 base_type,
1624 default,
1625 not_null,
1626 checks,
1627 }))
1628 }
1629
1630 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1634 let name = self.expect_ident_like()?;
1635 if !matches!(self.peek(), Token::As) {
1637 return Err(self.err(alloc::format!(
1638 "expected AS after CREATE TYPE {name:?}, got {:?}",
1639 self.peek()
1640 )));
1641 }
1642 self.advance();
1643 let kind_ident = match self.peek().clone() {
1645 Token::Ident(s) | Token::QuotedIdent(s) => s,
1646 other => {
1647 return Err(self.err(alloc::format!(
1648 "expected ENUM after CREATE TYPE {name:?} AS, got {other:?}"
1649 )));
1650 }
1651 };
1652 if !kind_ident.eq_ignore_ascii_case("enum") {
1653 return Err(self.err(alloc::format!(
1654 "Phase 1.4 only supports ENUM; got {kind_ident:?}"
1655 )));
1656 }
1657 self.advance();
1658 if !matches!(self.peek(), Token::LParen) {
1659 return Err(self.err(alloc::format!(
1660 "expected '(' after ENUM, got {:?}",
1661 self.peek()
1662 )));
1663 }
1664 self.advance();
1665 let mut labels: Vec<String> = Vec::new();
1666 loop {
1667 match self.peek().clone() {
1668 Token::String(s) => {
1669 self.advance();
1670 labels.push(s);
1671 }
1672 other => {
1673 return Err(
1674 self.err(alloc::format!("expected enum label string, got {other:?}"))
1675 );
1676 }
1677 }
1678 if matches!(self.peek(), Token::Comma) {
1679 self.advance();
1680 continue;
1681 }
1682 if matches!(self.peek(), Token::RParen) {
1683 self.advance();
1684 break;
1685 }
1686 return Err(self.err(alloc::format!(
1687 "expected , or ) in ENUM label list, got {:?}",
1688 self.peek()
1689 )));
1690 }
1691 if labels.is_empty() {
1692 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
1693 }
1694 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1695 name,
1696 kind: crate::ast::TypeKind::Enum { labels },
1697 }))
1698 }
1699
1700 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
1705 let if_not_exists = self.parse_if_not_exists();
1706 let name = self.expect_ident_like()?;
1707 let mut columns: Vec<String> = Vec::new();
1708 if matches!(self.peek(), Token::LParen) {
1709 self.advance();
1710 loop {
1711 let c = self.expect_ident_like()?;
1712 columns.push(c);
1713 if matches!(self.peek(), Token::Comma) {
1714 self.advance();
1715 continue;
1716 }
1717 if matches!(self.peek(), Token::RParen) {
1718 self.advance();
1719 break;
1720 }
1721 return Err(self.err(alloc::format!(
1722 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
1723 self.peek()
1724 )));
1725 }
1726 }
1727 if !matches!(self.peek(), Token::As) {
1728 return Err(self.err(alloc::format!(
1729 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
1730 self.peek()
1731 )));
1732 }
1733 self.advance();
1734 let body_stmt = self.parse_select_stmt()?;
1735 let Statement::Select(body) = body_stmt else {
1736 return Err(self.err(alloc::format!(
1737 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
1738 )));
1739 };
1740 let with_data = self.parse_optional_with_data(true)?;
1742 Ok(Statement::CreateMaterializedView(
1743 crate::ast::CreateMaterializedViewStatement {
1744 name,
1745 if_not_exists,
1746 columns,
1747 body,
1748 with_data,
1749 },
1750 ))
1751 }
1752
1753 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
1758 let save = self.pos;
1759 let is_with = match self.peek() {
1761 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
1762 _ => false,
1763 };
1764 if !is_with {
1765 return Ok(default_when_absent);
1766 }
1767 self.advance();
1768 let mut with_data = true;
1770 let is_no = match self.peek() {
1771 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
1772 _ => false,
1773 };
1774 if is_no {
1775 self.advance();
1776 with_data = false;
1777 }
1778 let is_data = match self.peek() {
1780 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
1781 _ => false,
1782 };
1783 if is_data {
1784 self.advance();
1785 Ok(with_data)
1786 } else {
1787 self.pos = save;
1790 Ok(default_when_absent)
1791 }
1792 }
1793
1794 fn parse_create_view_after_keyword(
1799 &mut self,
1800 or_replace: bool,
1801 _materialized_unused: bool,
1802 temporary: bool,
1803 ) -> Result<Statement, ParseError> {
1804 let if_not_exists = self.parse_if_not_exists();
1805 let name = self.expect_ident_like()?;
1806 let mut columns: Vec<String> = Vec::new();
1808 if matches!(self.peek(), Token::LParen) {
1809 self.advance();
1810 loop {
1811 let c = self.expect_ident_like()?;
1812 columns.push(c);
1813 if matches!(self.peek(), Token::Comma) {
1814 self.advance();
1815 continue;
1816 }
1817 if matches!(self.peek(), Token::RParen) {
1818 self.advance();
1819 break;
1820 }
1821 return Err(self.err(alloc::format!(
1822 "expected , or ) in VIEW column list, got {:?}",
1823 self.peek()
1824 )));
1825 }
1826 }
1827 if !matches!(self.peek(), Token::As) {
1829 return Err(self.err(alloc::format!(
1830 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
1831 self.peek()
1832 )));
1833 }
1834 self.advance();
1835 let body_stmt = self.parse_select_stmt()?;
1837 let Statement::Select(body) = body_stmt else {
1838 return Err(self.err(alloc::format!(
1839 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
1840 )));
1841 };
1842 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
1843 name,
1844 or_replace,
1845 if_not_exists,
1846 temporary,
1847 columns,
1848 body,
1849 }))
1850 }
1851
1852 fn parse_create_sequence_after_keyword(
1856 &mut self,
1857 temporary: bool,
1858 ) -> Result<Statement, ParseError> {
1859 let if_not_exists = self.parse_if_not_exists();
1860 let name = self.expect_ident_like()?;
1861 let data_type = if matches!(self.peek(), Token::As) {
1863 self.advance();
1864 Some(self.parse_sequence_data_type()?)
1865 } else {
1866 None
1867 };
1868 let options = self.parse_sequence_options(false)?;
1869 Ok(Statement::CreateSequence(
1870 crate::ast::CreateSequenceStatement {
1871 name,
1872 if_not_exists,
1873 temporary,
1874 data_type,
1875 options,
1876 },
1877 ))
1878 }
1879
1880 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
1883 let if_exists = self.parse_if_exists();
1884 let name = self.expect_ident_like()?;
1885 let options = self.parse_sequence_options(true)?;
1886 Ok(Statement::AlterSequence(
1887 crate::ast::AlterSequenceStatement {
1888 name,
1889 if_exists,
1890 options,
1891 },
1892 ))
1893 }
1894
1895 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
1896 let kw = self.expect_ident_like()?;
1897 match kw.to_ascii_lowercase().as_str() {
1898 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
1899 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
1900 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
1901 other => Err(self.err(alloc::format!(
1902 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
1903 ))),
1904 }
1905 }
1906
1907 fn parse_sequence_options(
1908 &mut self,
1909 allow_restart: bool,
1910 ) -> Result<crate::ast::SequenceOptions, ParseError> {
1911 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
1912 let mut opts = SequenceOptions::default();
1913 #[allow(clippy::while_let_loop)]
1914 loop {
1915 let kw_lc = match self.peek() {
1918 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
1919 _ => break,
1920 };
1921 match kw_lc.as_str() {
1922 "increment" => {
1923 self.advance();
1924 if matches!(self.peek(), Token::By) {
1926 self.advance();
1927 }
1928 opts.increment = Some(self.expect_signed_int()?);
1929 }
1930 "minvalue" => {
1931 self.advance();
1932 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
1933 }
1934 "maxvalue" => {
1935 self.advance();
1936 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
1937 }
1938 "no" => {
1939 self.advance();
1940 let what = self.expect_ident_like()?;
1941 match what.to_ascii_lowercase().as_str() {
1942 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
1943 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
1944 "cycle" => opts.cycle = Some(false),
1945 other => {
1946 return Err(self.err(alloc::format!(
1947 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
1948 )));
1949 }
1950 }
1951 }
1952 "start" => {
1953 self.advance();
1954 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1956 if s.eq_ignore_ascii_case("with"))
1957 {
1958 self.advance();
1959 }
1960 opts.start = Some(self.expect_signed_int()?);
1961 }
1962 "restart" if allow_restart => {
1963 self.advance();
1964 let mut with_val: Option<i64> = None;
1966 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1967 if s.eq_ignore_ascii_case("with"))
1968 {
1969 self.advance();
1970 with_val = Some(self.expect_signed_int()?);
1971 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
1972 with_val = Some(self.expect_signed_int()?);
1973 }
1974 opts.restart = Some(with_val);
1975 }
1976 "cache" => {
1977 self.advance();
1978 opts.cache = Some(self.expect_signed_int()?);
1979 }
1980 "cycle" => {
1981 self.advance();
1982 opts.cycle = Some(true);
1983 }
1984 "owned" => {
1985 self.advance();
1986 match self.peek() {
1988 Token::By => {
1989 self.advance();
1990 }
1991 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
1992 self.advance();
1993 }
1994 other => {
1995 return Err(
1996 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
1997 );
1998 }
1999 }
2000 let first = match self.advance() {
2004 Token::Ident(s) | Token::QuotedIdent(s) => s,
2005 other => {
2006 return Err(self.err(alloc::format!(
2007 "expected identifier or NONE after OWNED BY, got {other:?}"
2008 )));
2009 }
2010 };
2011 if first.eq_ignore_ascii_case("none") {
2012 opts.owned_by = Some(SequenceOwnedBy::None);
2013 } else if matches!(self.peek(), Token::Dot) {
2014 self.advance();
2015 let second = match self.advance() {
2016 Token::Ident(s) | Token::QuotedIdent(s) => s,
2017 other => {
2018 return Err(self.err(alloc::format!(
2019 "expected column name after OWNED BY {first}., got {other:?}"
2020 )));
2021 }
2022 };
2023 if matches!(self.peek(), Token::Dot) {
2032 self.advance();
2033 let third = match self.advance() {
2034 Token::Ident(s) | Token::QuotedIdent(s) => s,
2035 other => {
2036 return Err(self.err(alloc::format!(
2037 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2038 )));
2039 }
2040 };
2041 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2043 table: second,
2044 column: third,
2045 });
2046 } else {
2047 opts.owned_by = Some(SequenceOwnedBy::Column {
2048 table: first,
2049 column: second,
2050 });
2051 }
2052 } else {
2053 return Err(self.err(alloc::format!(
2054 "expected table.column or NONE after OWNED BY, got {first:?}"
2055 )));
2056 }
2057 }
2058 _ => break,
2059 }
2060 }
2061 Ok(opts)
2062 }
2063
2064 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2065 let neg = if matches!(self.peek(), Token::Minus) {
2066 self.advance();
2067 true
2068 } else {
2069 false
2070 };
2071 match self.peek() {
2072 Token::Integer(n) => {
2073 let v = *n;
2074 self.advance();
2075 Ok(if neg { -v } else { v })
2076 }
2077 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2078 }
2079 }
2080
2081 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2091 loop {
2092 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2094 self.advance();
2095 self.consume_optional_initially_clause()?;
2096 continue;
2097 }
2098 if matches!(self.peek(), Token::Not) {
2100 let look = self.tokens.get(self.pos + 1);
2101 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2102 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2105 continue;
2106 }
2107 break;
2108 }
2109 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2114 self.consume_optional_initially_clause()?;
2115 continue;
2116 }
2117 break;
2118 }
2119 Ok(())
2120 }
2121
2122 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2126 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2127 return Ok(());
2128 }
2129 self.advance(); match self.advance() {
2131 Token::Ident(s)
2132 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2133 {
2134 Ok(())
2135 }
2136 other => Err(self.err(alloc::format!(
2137 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2138 ))),
2139 }
2140 }
2141
2142 fn consume_mysql_routine_body(&mut self) {
2160 let mut depth: i32 = 0;
2165 let mut started = false;
2166 loop {
2167 match self.peek().clone() {
2168 Token::Begin => {
2169 self.advance();
2170 depth += 1;
2171 started = true;
2172 }
2173 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2174 self.advance();
2175 if started {
2176 depth -= 1;
2177 if depth <= 0 {
2178 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2185 let is_inner_close = matches!(
2190 self.peek(),
2191 Token::Ident(s) | Token::QuotedIdent(s)
2192 if matches!(
2193 s.to_ascii_lowercase().as_str(),
2194 "if" | "loop" | "while" | "case" | "repeat"
2195 )
2196 );
2197 if is_inner_close {
2198 self.advance();
2199 depth += 1;
2200 continue;
2201 }
2202 }
2203 if matches!(self.peek(), Token::Semicolon) {
2205 self.advance();
2206 }
2207 return;
2208 }
2209 }
2210 }
2211 Token::Eof => return,
2212 _ => {
2213 self.advance();
2214 }
2215 }
2216 }
2217 }
2218
2219 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2233 loop {
2234 match self.peek().clone() {
2235 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2236 self.advance(); if matches!(self.peek(), Token::Eq) {
2240 self.advance();
2241 }
2242 if matches!(
2246 self.peek(),
2247 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2248 ) {
2249 self.advance();
2250 }
2251 }
2252 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2253 self.advance(); if matches!(self.peek(), Token::Eq) {
2255 self.advance();
2256 }
2257 match self.peek().clone() {
2260 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2261 self.advance();
2262 if matches!(self.peek(), Token::At) {
2264 self.advance();
2265 if matches!(
2266 self.peek(),
2267 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2268 ) {
2269 self.advance();
2270 }
2271 }
2272 }
2273 _ => {}
2274 }
2275 }
2276 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2277 let save = self.pos;
2282 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2284 if s2.eq_ignore_ascii_case("security"))
2285 {
2286 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2289 self.advance();
2290 }
2291 } else {
2292 self.pos = save;
2295 return Ok(());
2296 }
2297 }
2298 _ => return Ok(()),
2299 }
2300 }
2301 }
2302
2303 fn parse_if_not_exists(&mut self) -> bool {
2304 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2305 {
2306 let save = self.pos;
2307 self.advance();
2308 if matches!(self.peek(), Token::Not) {
2309 self.advance();
2310 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2311 {
2312 self.advance();
2313 return true;
2314 }
2315 }
2316 self.pos = save;
2317 }
2318 false
2319 }
2320
2321 fn parse_if_exists(&mut self) -> bool {
2322 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2323 {
2324 let save = self.pos;
2325 self.advance();
2326 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2327 {
2328 self.advance();
2329 return true;
2330 }
2331 self.pos = save;
2332 }
2333 false
2334 }
2335
2336 fn parse_create_trigger_after_keyword(
2340 &mut self,
2341 or_replace: bool,
2342 ) -> Result<Statement, ParseError> {
2343 let name = self.expect_ident_like()?;
2344 let timing = {
2345 let ident = self.expect_ident_like()?;
2346 if ident.eq_ignore_ascii_case("before") {
2347 TriggerTiming::Before
2348 } else if ident.eq_ignore_ascii_case("after") {
2349 TriggerTiming::After
2350 } else if ident.eq_ignore_ascii_case("instead") {
2351 let next = self.expect_ident_like()?;
2352 if !next.eq_ignore_ascii_case("of") {
2353 return Err(self.err(alloc::format!(
2354 "expected OF after INSTEAD in trigger timing, got {next:?}"
2355 )));
2356 }
2357 TriggerTiming::InsteadOf
2358 } else {
2359 return Err(self.err(alloc::format!(
2360 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2361 )));
2362 }
2363 };
2364 let mut events: Vec<TriggerEvent> = Vec::new();
2371 let mut update_columns: Vec<String> = Vec::new();
2372 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2373 events.push(first_ev);
2374 if !first_cols.is_empty() {
2375 update_columns = first_cols;
2376 }
2377 while matches!(self.peek(), Token::Or) {
2378 self.advance();
2379 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2380 events.push(ev);
2381 if !cols.is_empty() {
2382 if !update_columns.is_empty() {
2383 return Err(
2384 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2385 );
2386 }
2387 update_columns = cols;
2388 }
2389 }
2390 let tok = self.peek();
2392 let Token::On = tok else {
2393 return Err(self.err(alloc::format!(
2394 "expected ON after trigger events, got {tok:?}"
2395 )));
2396 };
2397 self.advance();
2398 let table = self.expect_ident_like()?;
2399 if !matches!(self.peek(), Token::For) {
2403 return Err(self.err(alloc::format!(
2404 "expected FOR EACH ROW / STATEMENT, got {:?}",
2405 self.peek()
2406 )));
2407 }
2408 self.advance();
2409 let for_each = {
2410 let e = self.expect_ident_like()?;
2411 if !e.eq_ignore_ascii_case("each") {
2412 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2413 }
2414 let unit = self.expect_ident_like()?;
2415 if unit.eq_ignore_ascii_case("row") {
2416 TriggerForEach::Row
2417 } else if unit.eq_ignore_ascii_case("statement") {
2418 TriggerForEach::Statement
2419 } else {
2420 return Err(self.err(alloc::format!(
2421 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2422 )));
2423 }
2424 };
2425 let exec = self.expect_ident_like()?;
2427 if !exec.eq_ignore_ascii_case("execute") {
2428 return Err(self.err(alloc::format!(
2429 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2430 )));
2431 }
2432 let fn_or_proc = self.expect_ident_like()?;
2433 if !(fn_or_proc.eq_ignore_ascii_case("function")
2434 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2435 {
2436 return Err(self.err(alloc::format!(
2437 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2438 )));
2439 }
2440 let function = self.expect_ident_like()?;
2441 if matches!(self.peek(), Token::LParen) {
2443 self.advance();
2444 if !matches!(self.peek(), Token::RParen) {
2445 return Err(self.err(alloc::format!(
2446 "v7.12.4 trigger function calls take no args; got {:?}",
2447 self.peek()
2448 )));
2449 }
2450 self.advance();
2451 }
2452 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2453 name,
2454 or_replace,
2455 timing,
2456 events,
2457 table,
2458 for_each,
2459 function,
2460 update_columns,
2461 }))
2462 }
2463
2464 fn parse_trigger_event_with_optional_of(
2468 &mut self,
2469 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2470 let ev = self.parse_trigger_event()?;
2471 if !matches!(ev, TriggerEvent::Update) {
2472 return Ok((ev, Vec::new()));
2473 }
2474 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2476 return Ok((ev, Vec::new()));
2477 }
2478 self.advance(); let mut cols: Vec<String> = Vec::new();
2480 loop {
2481 cols.push(self.expect_ident_like()?);
2482 if matches!(self.peek(), Token::Comma) {
2483 self.advance();
2484 continue;
2485 }
2486 break;
2487 }
2488 if cols.is_empty() {
2489 return Err(
2490 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2491 );
2492 }
2493 Ok((ev, cols))
2494 }
2495
2496 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2503 let declarations = if matches!(
2505 self.peek(),
2506 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2507 ) {
2508 self.advance();
2509 self.parse_plpgsql_declare_block()?
2510 } else {
2511 Vec::new()
2512 };
2513 if !matches!(self.peek(), Token::Begin) {
2518 return Err(self.err(alloc::format!(
2519 "expected BEGIN at start of plpgsql block, got {:?}",
2520 self.peek()
2521 )));
2522 }
2523 self.advance();
2524 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2525 Ok(PlPgSqlBlock {
2526 declarations,
2527 statements,
2528 })
2529 }
2530
2531 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2535 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2536 loop {
2537 if matches!(self.peek(), Token::Begin) {
2538 return Ok(out);
2539 }
2540 let name = self.expect_ident_like()?;
2541 let ty_token = self.expect_ident_like()?;
2542 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2543 Some(t) => FunctionArgType::Typed(t),
2544 None => FunctionArgType::Raw(ty_token),
2545 };
2546 let default = match self.peek() {
2547 Token::ColonEq => {
2548 self.advance();
2549 Some(self.parse_expr(0)?)
2550 }
2551 Token::Eq => {
2552 self.advance();
2556 Some(self.parse_expr(0)?)
2557 }
2558 _ => None,
2559 };
2560 if !matches!(self.peek(), Token::Semicolon) {
2562 return Err(self.err(alloc::format!(
2563 "expected ; after DECLARE entry for {name:?}, got {:?}",
2564 self.peek()
2565 )));
2566 }
2567 self.advance();
2568 out.push(PlPgSqlDeclare { name, ty, default });
2569 }
2570 }
2571
2572 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2577 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2578 loop {
2579 while matches!(self.peek(), Token::Semicolon) {
2581 self.advance();
2582 }
2583 if matches!(
2585 self.peek(),
2586 Token::Ident(s) | Token::QuotedIdent(s)
2587 if s.eq_ignore_ascii_case("end")
2588 || s.eq_ignore_ascii_case("else")
2589 || s.eq_ignore_ascii_case("elsif")
2590 || s.eq_ignore_ascii_case("elseif")
2591 ) {
2592 return Ok(statements);
2593 }
2594 let stmt = self.parse_plpgsql_stmt()?;
2597 statements.push(stmt);
2598 match self.peek() {
2599 Token::Semicolon => {
2600 self.advance();
2601 }
2602 Token::Ident(s) | Token::QuotedIdent(s)
2603 if s.eq_ignore_ascii_case("end")
2604 || s.eq_ignore_ascii_case("else")
2605 || s.eq_ignore_ascii_case("elsif")
2606 || s.eq_ignore_ascii_case("elseif") =>
2607 {
2608 }
2610 other => {
2611 return Err(self.err(alloc::format!(
2612 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2613 )));
2614 }
2615 }
2616 }
2617 }
2618
2619 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2620 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2622 {
2623 self.advance();
2624 return self.parse_plpgsql_return();
2625 }
2626 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2628 {
2629 self.advance();
2630 return self.parse_plpgsql_if();
2631 }
2632 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2634 {
2635 self.advance();
2636 return self.parse_plpgsql_raise();
2637 }
2638 if matches!(self.peek(), Token::Select)
2649 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2650 {
2651 return Ok(PlPgSqlStmt::SelectInto {
2652 var: var_name,
2653 body: Box::new(select_body),
2654 });
2655 }
2656 if matches!(self.peek(), Token::Insert)
2666 || matches!(self.peek(), Token::Select)
2667 || matches!(self.peek(), Token::Create)
2668 || matches!(self.peek(), Token::Drop)
2669 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2670 if s.eq_ignore_ascii_case("update")
2671 || s.eq_ignore_ascii_case("delete")
2672 || s.eq_ignore_ascii_case("alter"))
2673 {
2674 let stmt = self.parse_one_statement()?;
2675 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2676 }
2677 let target = self.parse_plpgsql_assign_target()?;
2680 match self.peek() {
2683 Token::ColonEq => {
2684 self.advance();
2685 }
2686 Token::Colon => {
2687 self.advance();
2688 if !matches!(self.peek(), Token::Eq) {
2689 return Err(self.err(alloc::format!(
2690 "expected := after plpgsql assign target, got `:` then {:?}",
2691 self.peek()
2692 )));
2693 }
2694 self.advance();
2695 }
2696 other => {
2697 return Err(self.err(alloc::format!(
2698 "expected := after plpgsql assign target, got {other:?}"
2699 )));
2700 }
2701 }
2702 let value = self.parse_expr(0)?;
2703 Ok(PlPgSqlStmt::Assign { target, value })
2704 }
2705
2706 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2709 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
2710 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
2711 loop {
2712 let cond = self.parse_expr(0)?;
2714 let then_kw = self.expect_ident_like()?;
2715 if !then_kw.eq_ignore_ascii_case("then") {
2716 return Err(self.err(alloc::format!(
2717 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
2718 )));
2719 }
2720 let body = self.parse_plpgsql_stmt_list_until_end()?;
2721 branches.push((cond, body));
2722 match self.peek() {
2724 Token::Ident(s) | Token::QuotedIdent(s)
2725 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
2726 {
2727 self.advance();
2728 continue;
2729 }
2730 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
2731 self.advance();
2732 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
2733 break;
2734 }
2735 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2736 break;
2737 }
2738 other => {
2739 return Err(self.err(alloc::format!(
2740 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
2741 )));
2742 }
2743 }
2744 }
2745 let end_kw = self.expect_ident_like()?;
2748 if !end_kw.eq_ignore_ascii_case("end") {
2749 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
2750 }
2751 let if_kw = self.expect_ident_like()?;
2752 if !if_kw.eq_ignore_ascii_case("if") {
2753 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
2754 }
2755 Ok(PlPgSqlStmt::If {
2756 branches,
2757 else_branch,
2758 })
2759 }
2760
2761 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2765 let lvl_ident = self.expect_ident_like()?;
2766 let level = match lvl_ident.to_ascii_lowercase().as_str() {
2767 "notice" => RaiseLevel::Notice,
2768 "warning" => RaiseLevel::Warning,
2769 "info" => RaiseLevel::Info,
2770 "log" => RaiseLevel::Log,
2771 "debug" => RaiseLevel::Debug,
2772 "exception" => RaiseLevel::Exception,
2773 other => {
2774 return Err(self.err(alloc::format!(
2775 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
2776 )));
2777 }
2778 };
2779 let Token::String(msg) = self.peek() else {
2783 return Err(self.err(alloc::format!(
2784 "expected RAISE message string, got {:?}",
2785 self.peek()
2786 )));
2787 };
2788 let message = msg.clone();
2789 self.advance();
2790 let mut args: Vec<Expr> = Vec::new();
2792 while matches!(self.peek(), Token::Comma) {
2793 self.advance();
2794 args.push(self.parse_expr(0)?);
2795 }
2796 Ok(PlPgSqlStmt::Raise {
2797 level,
2798 message,
2799 args,
2800 })
2801 }
2802
2803 #[allow(clippy::too_many_lines)]
2811 fn try_parse_plpgsql_select_into(
2812 &mut self,
2813 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
2814 let start = self.pos;
2819 let mut into_pos: Option<usize> = None;
2820 let mut depth: i32 = 0;
2821 let mut i = start + 1;
2822 while i < self.tokens.len() {
2823 match &self.tokens[i] {
2824 Token::LParen => depth += 1,
2825 Token::RParen => depth -= 1,
2826 Token::Semicolon if depth == 0 => break,
2827 Token::Ident(s)
2828 if depth == 0
2829 && (s.eq_ignore_ascii_case("end")
2830 || s.eq_ignore_ascii_case("else")
2831 || s.eq_ignore_ascii_case("elsif")) =>
2832 {
2833 break;
2834 }
2835 Token::Into if depth == 0 => {
2836 into_pos = Some(i);
2837 break;
2838 }
2839 _ => {}
2840 }
2841 i += 1;
2842 }
2843 let Some(into_at) = into_pos else {
2844 return Ok(None);
2845 };
2846 let var = match self.tokens.get(into_at + 1) {
2850 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
2851 other => {
2852 return Err(self.err(alloc::format!(
2853 "expected variable name after SELECT … INTO, got {other:?}"
2854 )));
2855 }
2856 };
2857 let mut end = into_at + 2;
2860 let mut depth2: i32 = 0;
2861 while end < self.tokens.len() {
2862 match &self.tokens[end] {
2863 Token::LParen => depth2 += 1,
2864 Token::RParen => depth2 -= 1,
2865 Token::Semicolon if depth2 == 0 => break,
2866 Token::Ident(s)
2867 if depth2 == 0
2868 && (s.eq_ignore_ascii_case("end")
2869 || s.eq_ignore_ascii_case("else")
2870 || s.eq_ignore_ascii_case("elsif")) =>
2871 {
2872 break;
2873 }
2874 _ => {}
2875 }
2876 end += 1;
2877 }
2878 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
2883 for j in start..into_at {
2884 rebuilt.push(self.tokens[j].clone());
2885 }
2886 for j in (into_at + 2)..end {
2887 rebuilt.push(self.tokens[j].clone());
2888 }
2889 rebuilt.push(Token::Eof);
2890 let saved_pos = self.pos;
2891 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
2892 self.pos = 0;
2893 if !matches!(self.peek(), Token::Select) {
2895 self.tokens = saved_tokens;
2896 self.pos = saved_pos;
2897 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
2898 }
2899 let sel = self.parse_select_stmt();
2900 self.tokens = saved_tokens;
2901 self.pos = end;
2902 let sel = sel?;
2903 let Statement::Select(body) = sel else {
2904 return Err(self.err(alloc::format!(
2905 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
2906 )));
2907 };
2908 Ok(Some((body, var)))
2909 }
2910
2911 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
2912 let head = match self.advance() {
2926 Token::Ident(s) | Token::QuotedIdent(s) => s,
2927 other => {
2928 return Err(self.err(alloc::format!(
2929 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
2930 )));
2931 }
2932 };
2933 if matches!(self.peek(), Token::Dot) {
2934 self.advance();
2935 let col = self.expect_ident_like()?;
2936 if head.eq_ignore_ascii_case("new") {
2937 return Ok(AssignTarget::NewColumn(col));
2938 }
2939 if head.eq_ignore_ascii_case("old") {
2940 return Ok(AssignTarget::OldColumn(col));
2941 }
2942 return Err(self.err(alloc::format!(
2943 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
2944 got {head:?}.<col>"
2945 )));
2946 }
2947 Ok(AssignTarget::Local(head))
2948 }
2949
2950 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2951 match self.peek() {
2953 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
2954 self.advance();
2955 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
2956 }
2957 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
2958 self.advance();
2959 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
2960 }
2961 Token::Null => {
2962 self.advance();
2963 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
2964 }
2965 Token::Semicolon => {
2968 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
2969 }
2970 _ => {}
2971 }
2972 let e = self.parse_expr(0)?;
2974 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
2975 }
2976
2977 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
2978 if matches!(self.peek(), Token::Insert) {
2983 self.advance();
2984 return Ok(TriggerEvent::Insert);
2985 }
2986 match self.peek() {
2987 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
2988 self.advance();
2989 Ok(TriggerEvent::Update)
2990 }
2991 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
2992 self.advance();
2993 Ok(TriggerEvent::Delete)
2994 }
2995 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
2996 self.advance();
2997 Ok(TriggerEvent::Truncate)
2998 }
2999 other => Err(self.err(alloc::format!(
3000 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3001 ))),
3002 }
3003 }
3004
3005 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3012 let name = self.expect_ident_or_string()?;
3013 let scope = if matches!(self.peek(), Token::For) {
3016 self.advance();
3017 if matches!(self.peek(), Token::All) {
3018 self.advance();
3019 if !matches!(self.peek(), Token::Tables) {
3020 return Err(self.err(format!(
3021 "expected TABLES after FOR ALL, got {:?}",
3022 self.peek()
3023 )));
3024 }
3025 self.advance();
3026 if matches!(self.peek(), Token::Except) {
3027 self.advance();
3028 let tables = self.parse_publication_table_list()?;
3029 PublicationScope::AllTablesExcept(tables)
3030 } else {
3031 PublicationScope::AllTables
3032 }
3033 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3034 self.advance();
3037 let tables = self.parse_publication_table_list()?;
3038 PublicationScope::ForTables(tables)
3039 } else {
3040 return Err(self.err(format!(
3041 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3042 self.peek()
3043 )));
3044 }
3045 } else {
3046 PublicationScope::AllTables
3047 };
3048 Ok(Statement::CreatePublication(CreatePublicationStatement {
3049 name,
3050 scope,
3051 }))
3052 }
3053
3054 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3059 let first = self.expect_ident_like()?;
3060 let mut out = alloc::vec![first];
3061 while matches!(self.peek(), Token::Comma) {
3062 self.advance();
3063 out.push(self.expect_ident_like()?);
3064 }
3065 Ok(out)
3066 }
3067
3068 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3076 let name = self.expect_ident_or_string()?;
3077 if !matches!(self.peek(), Token::Connection) {
3078 return Err(self.err(format!(
3079 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3080 self.peek()
3081 )));
3082 }
3083 self.advance();
3084 let conn_str = self.expect_string_literal()?;
3085 if !matches!(self.peek(), Token::Publication) {
3086 return Err(self.err(format!(
3087 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3088 self.peek()
3089 )));
3090 }
3091 self.advance();
3092 let first = self.expect_ident_like()?;
3095 let mut publications = alloc::vec![first];
3096 while matches!(self.peek(), Token::Comma) {
3097 self.advance();
3098 publications.push(self.expect_ident_like()?);
3099 }
3100 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3101 name,
3102 conn_str,
3103 publications,
3104 }))
3105 }
3106
3107 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3114 let mut name = self.expect_ident_like()?;
3115 while matches!(self.peek(), Token::Dot) {
3116 self.advance();
3117 let next = self.expect_ident_like()?;
3118 name.push('.');
3119 name.push_str(&next);
3120 }
3121 Ok(name.to_ascii_lowercase())
3122 }
3123
3124 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3125 match self.advance() {
3126 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3127 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3128 Ok(crate::ast::SetValue::Default)
3129 }
3130 Token::Ident(s) | Token::QuotedIdent(s) => {
3131 let mut accum = s;
3132 while matches!(self.peek(), Token::Dot) {
3133 self.advance();
3134 let next = self.expect_ident_like()?;
3135 accum.push('.');
3136 accum.push_str(&next);
3137 }
3138 Ok(crate::ast::SetValue::Ident(accum))
3139 }
3140 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3141 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3142 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3148 Token::Minus => match self.advance() {
3153 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3154 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3155 other => Err(self.err(format!(
3156 "expected numeric after `-` in SET value, got {other:?}"
3157 ))),
3158 },
3159 other => Err(self.err(format!(
3160 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3161 ))),
3162 }
3163 }
3164
3165 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3166 if !matches!(self.peek(), Token::For) {
3170 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3171 }
3172 self.advance();
3173 self.expect_keyword_ident("wal")?;
3174 self.expect_keyword_ident("position")?;
3175 let pos = self.expect_u64_literal()?;
3176 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3177 {
3178 self.advance();
3179 self.expect_keyword_ident("timeout")?;
3180 Some(self.expect_u64_literal()?)
3181 } else {
3182 None
3183 };
3184 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3185 }
3186
3187 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3191 match self.advance() {
3192 Token::Integer(n) if n >= 0 => Ok(n as u64),
3193 Token::Integer(n) => Err(ParseError {
3194 message: format!("expected non-negative integer, got {n}"),
3195 token_pos: self.pos.saturating_sub(1),
3196 }),
3197 other => Err(ParseError {
3198 message: format!("expected integer literal, got {other:?}"),
3199 token_pos: self.pos.saturating_sub(1),
3200 }),
3201 }
3202 }
3203
3204 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3208 let name = self.expect_ident_or_string()?;
3209 self.expect_keyword_ident("with")?;
3210 self.expect_keyword_ident("password")?;
3211 let password = self.expect_string_literal()?;
3212 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3213 && s.eq_ignore_ascii_case("role")
3214 {
3215 self.advance();
3216 self.expect_string_literal()?
3217 } else {
3218 "readonly".to_string()
3219 };
3220 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3221 name,
3222 password,
3223 role,
3224 }))
3225 }
3226
3227 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3230 let table = self.expect_ident_like()?;
3231 self.expect_keyword_ident("set")?;
3232 let mut assignments = Vec::new();
3233 loop {
3234 let col = self.expect_ident_like()?;
3235 if !matches!(self.peek(), Token::Eq) {
3236 return Err(self.err(format!(
3237 "expected `=` after column name in UPDATE SET, got {:?}",
3238 self.peek()
3239 )));
3240 }
3241 self.advance();
3242 let value = self.parse_expr(0)?;
3243 assignments.push((col, value));
3244 if matches!(self.peek(), Token::Comma) {
3245 self.advance();
3246 continue;
3247 }
3248 break;
3249 }
3250 let where_ = if matches!(self.peek(), Token::Where) {
3251 self.advance();
3252 Some(self.parse_expr(0)?)
3253 } else {
3254 None
3255 };
3256 let returning = self.parse_optional_returning()?;
3257 Ok(Statement::Update(crate::ast::UpdateStatement {
3258 table,
3259 assignments,
3260 where_,
3261 returning,
3262 }))
3263 }
3264
3265 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3268 if !matches!(self.peek(), Token::From) {
3269 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3270 }
3271 self.advance();
3272 let table = self.expect_ident_like()?;
3273 let where_ = if matches!(self.peek(), Token::Where) {
3274 self.advance();
3275 Some(self.parse_expr(0)?)
3276 } else {
3277 None
3278 };
3279 let returning = self.parse_optional_returning()?;
3280 Ok(Statement::Delete(crate::ast::DeleteStatement {
3281 table,
3282 where_,
3283 returning,
3284 }))
3285 }
3286
3287 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3297 let is_into_kw = matches!(self.peek(), Token::Into)
3299 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3300 if !is_into_kw {
3301 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3302 }
3303 self.advance();
3304 let target = self.expect_ident_like()?;
3305 let target_alias = match self.peek() {
3307 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3308 Some(self.expect_ident_like()?)
3309 }
3310 _ => None,
3311 };
3312 let is_using_kw = matches!(
3314 self.peek(),
3315 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3316 );
3317 if !is_using_kw {
3318 return Err(self.err(format!(
3319 "expected USING after MERGE INTO target, got {:?}",
3320 self.peek()
3321 )));
3322 }
3323 self.advance();
3324 let source = self.expect_ident_like()?;
3325 let source_alias = match self.peek() {
3326 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3327 Some(self.expect_ident_like()?)
3328 }
3329 _ => None,
3330 };
3331 if !matches!(self.peek(), Token::On) {
3333 return Err(self.err(format!(
3334 "expected ON after MERGE … USING source, got {:?}",
3335 self.peek()
3336 )));
3337 }
3338 self.advance();
3339 let on = self.parse_expr(0)?;
3340 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3342 loop {
3343 let is_when_kw = matches!(
3344 self.peek(),
3345 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3346 );
3347 if !is_when_kw {
3348 break;
3349 }
3350 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3353 self.advance();
3354 crate::ast::MergeMatched::NotMatched
3355 } else {
3356 crate::ast::MergeMatched::Matched
3357 };
3358 let is_matched_kw = matches!(
3359 self.peek(),
3360 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3361 );
3362 if !is_matched_kw {
3363 return Err(self.err(format!(
3364 "expected MATCHED in WHEN clause, got {:?}",
3365 self.peek()
3366 )));
3367 }
3368 self.advance();
3369 let condition = if matches!(self.peek(), Token::And) {
3371 self.advance();
3372 Some(self.parse_expr(0)?)
3373 } else {
3374 None
3375 };
3376 let is_then_kw = matches!(
3378 self.peek(),
3379 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3380 );
3381 if !is_then_kw {
3382 return Err(self.err(format!(
3383 "expected THEN in WHEN clause, got {:?}",
3384 self.peek()
3385 )));
3386 }
3387 self.advance();
3388 let action = match self.peek().clone() {
3390 Token::Insert => {
3391 self.advance();
3392 if !matches!(self.peek(), Token::LParen) {
3394 return Err(self.err(format!(
3395 "expected '(' after INSERT in MERGE, got {:?}",
3396 self.peek()
3397 )));
3398 }
3399 self.advance();
3400 let mut columns: Vec<String> = Vec::new();
3401 loop {
3402 columns.push(self.expect_ident_like()?);
3403 if matches!(self.peek(), Token::Comma) {
3404 self.advance();
3405 continue;
3406 }
3407 break;
3408 }
3409 if !matches!(self.peek(), Token::RParen) {
3410 return Err(self.err(format!(
3411 "expected ')' after INSERT column list, got {:?}",
3412 self.peek()
3413 )));
3414 }
3415 self.advance();
3416 if !matches!(self.peek(), Token::Values) {
3418 return Err(self.err(format!(
3419 "expected VALUES in MERGE INSERT, got {:?}",
3420 self.peek()
3421 )));
3422 }
3423 self.advance();
3424 if !matches!(self.peek(), Token::LParen) {
3425 return Err(self.err(format!(
3426 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3427 self.peek()
3428 )));
3429 }
3430 self.advance();
3431 let mut values: Vec<crate::ast::Expr> = Vec::new();
3432 loop {
3433 values.push(self.parse_expr(0)?);
3434 if matches!(self.peek(), Token::Comma) {
3435 self.advance();
3436 continue;
3437 }
3438 break;
3439 }
3440 if !matches!(self.peek(), Token::RParen) {
3441 return Err(self.err(format!(
3442 "expected ')' after MERGE INSERT values, got {:?}",
3443 self.peek()
3444 )));
3445 }
3446 self.advance();
3447 if columns.len() != values.len() {
3448 return Err(self.err(format!(
3449 "MERGE INSERT column count ({}) ≠ value count ({})",
3450 columns.len(),
3451 values.len()
3452 )));
3453 }
3454 crate::ast::MergeAction::Insert { columns, values }
3455 }
3456 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3457 self.advance();
3458 let is_set_kw = matches!(
3460 self.peek(),
3461 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3462 );
3463 if !is_set_kw {
3464 return Err(self.err(format!(
3465 "expected SET after UPDATE in MERGE, got {:?}",
3466 self.peek()
3467 )));
3468 }
3469 self.advance();
3470 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3471 loop {
3472 let col = self.expect_ident_like()?;
3473 if !matches!(self.peek(), Token::Eq) {
3474 return Err(self.err(format!(
3475 "expected '=' in MERGE UPDATE assignment, got {:?}",
3476 self.peek()
3477 )));
3478 }
3479 self.advance();
3480 let expr = self.parse_expr(0)?;
3481 assignments.push((col, expr));
3482 if matches!(self.peek(), Token::Comma) {
3483 self.advance();
3484 continue;
3485 }
3486 break;
3487 }
3488 crate::ast::MergeAction::Update { assignments }
3489 }
3490 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3491 self.advance();
3492 crate::ast::MergeAction::Delete
3493 }
3494 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3495 self.advance();
3496 let is_nothing_kw = matches!(
3497 self.peek(),
3498 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3499 );
3500 if !is_nothing_kw {
3501 return Err(self.err(format!(
3502 "expected NOTHING after DO in MERGE clause, got {:?}",
3503 self.peek()
3504 )));
3505 }
3506 self.advance();
3507 crate::ast::MergeAction::DoNothing
3508 }
3509 other => {
3510 return Err(self.err(format!(
3511 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3512 )));
3513 }
3514 };
3515 clauses.push(crate::ast::MergeWhenClause {
3516 matched,
3517 condition,
3518 action,
3519 });
3520 }
3521 if clauses.is_empty() {
3522 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3523 }
3524 Ok(Statement::Merge(crate::ast::MergeStatement {
3525 target,
3526 target_alias,
3527 source,
3528 source_alias,
3529 on,
3530 clauses,
3531 }))
3532 }
3533
3534 fn parse_optional_returning(
3539 &mut self,
3540 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3541 let is_returning_kw = matches!(
3542 self.peek(),
3543 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3544 );
3545 if !is_returning_kw {
3546 return Ok(None);
3547 }
3548 self.advance();
3549 let mut items = Vec::new();
3550 loop {
3551 items.push(self.parse_select_item()?);
3552 if matches!(self.peek(), Token::Comma) {
3553 self.advance();
3554 continue;
3555 }
3556 break;
3557 }
3558 Ok(Some(items))
3559 }
3560
3561 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
3569 match self.advance() {
3576 Token::Index => {}
3577 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
3578 Token::Table => {
3581 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3582 self.advance();
3583 }
3584 return self.parse_alter_table_after_keyword();
3585 }
3586 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
3587 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3588 self.advance();
3589 }
3590 return self.parse_alter_table_after_keyword();
3591 }
3592 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
3595 return self.parse_alter_sequence_after_keyword();
3596 }
3597 Token::Ident(s) | Token::QuotedIdent(s)
3603 if matches!(
3604 s.to_ascii_lowercase().as_str(),
3605 "view"
3606 | "function"
3607 | "type"
3608 | "domain"
3609 | "database"
3610 | "role"
3611 | "schema"
3612 | "owner"
3613 | "default"
3614 | "extension"
3615 | "materialized"
3616 | "policy"
3617 | "publication"
3618 | "subscription"
3619 ) =>
3620 {
3621 self.consume_until_statement_boundary();
3622 return Ok(Statement::Empty);
3623 }
3624 other => {
3625 return Err(self.err(format!(
3626 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
3627 after ALTER, got {other:?}"
3628 )));
3629 }
3630 }
3631 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3636 let next = self.tokens.get(self.pos + 1);
3637 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3638 self.advance();
3639 self.advance();
3640 true
3641 } else {
3642 false
3643 }
3644 } else {
3645 false
3646 };
3647 let name = self.expect_ident_like()?;
3648 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
3652 self.advance();
3653 if matches!(self.peek(), Token::To) {
3654 self.advance();
3655 } else {
3656 self.expect_keyword_ident("to")?;
3657 }
3658 let new = self.expect_ident_like()?;
3659 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3660 name,
3661 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
3662 }));
3663 }
3664 self.expect_keyword_ident("rebuild")?;
3666 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3668 self.advance();
3669 if !matches!(self.peek(), Token::LParen) {
3670 return Err(self.err(format!(
3671 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
3672 self.peek()
3673 )));
3674 }
3675 self.advance();
3676 self.expect_keyword_ident("encoding")?;
3677 if !matches!(self.peek(), Token::Eq) {
3678 return Err(self.err(format!(
3679 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
3680 self.peek()
3681 )));
3682 }
3683 self.advance();
3684 let enc_ident = match self.advance() {
3685 Token::Ident(s) | Token::QuotedIdent(s) => s,
3686 other => {
3687 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
3688 }
3689 };
3690 let enc = match enc_ident.to_ascii_lowercase().as_str() {
3691 "f32" => VecEncoding::F32,
3692 "sq8" => VecEncoding::Sq8,
3693 "half" => VecEncoding::F16,
3694 other => {
3695 return Err(self.err(format!(
3696 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
3697 )));
3698 }
3699 };
3700 if !matches!(self.peek(), Token::RParen) {
3701 return Err(self.err(format!(
3702 "expected ')' after encoding value, got {:?}",
3703 self.peek()
3704 )));
3705 }
3706 self.advance();
3707 Some(enc)
3708 } else {
3709 None
3710 };
3711 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3712 name,
3713 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
3714 }))
3715 }
3716
3717 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
3723 let table_name = self.expect_ident_like()?;
3724 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
3725 loop {
3726 let subaction = self.parse_alter_table_subaction()?;
3727 targets.extend(subaction);
3731 if matches!(self.peek(), Token::Comma) {
3732 self.advance();
3733 continue;
3734 }
3735 break;
3736 }
3737 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
3738 name: table_name,
3739 targets,
3740 }))
3741 }
3742
3743 fn parse_alter_table_subaction(
3747 &mut self,
3748 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
3749 match self.peek() {
3750 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
3751 self.advance();
3752 let setting = self.expect_ident_like()?;
3753 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
3754 return Err(self.err(alloc::format!(
3755 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
3756 )));
3757 }
3758 if !matches!(self.peek(), Token::Eq) {
3759 return Err(self.err(alloc::format!(
3760 "expected '=' after hot_tier_bytes, got {:?}",
3761 self.peek()
3762 )));
3763 }
3764 self.advance();
3765 let n = self.expect_u64_literal()?;
3766 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
3767 }
3768 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
3769 self.advance();
3770 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
3777 {
3778 let kind_pos = self.pos + 2;
3781 let kind = self.tokens.get(kind_pos).cloned();
3782 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
3783 {
3784 let fk = self.parse_table_level_fk()?;
3785 return Ok(alloc::vec![
3786 crate::ast::AlterTableTarget::AddForeignKey(fk)
3787 ]);
3788 }
3789 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
3790 {
3791 self.advance(); let _name = self.expect_ident_like()?;
3793 self.advance(); self.expect_keyword_ident("key")?;
3795 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3796 return Ok(alloc::vec![
3797 crate::ast::AlterTableTarget::AddTableConstraint(
3798 crate::ast::TableConstraint::PrimaryKey {
3799 name: None,
3800 columns: cols,
3801 }
3802 )
3803 ]);
3804 }
3805 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
3806 {
3807 self.advance(); let _name = self.expect_ident_like()?;
3809 self.advance(); let cols = self.parse_paren_ident_list("UNIQUE")?;
3811 return Ok(alloc::vec![
3812 crate::ast::AlterTableTarget::AddTableConstraint(
3813 crate::ast::TableConstraint::Unique {
3814 name: None,
3815 columns: cols,
3816 nulls_not_distinct: false,
3817 }
3818 )
3819 ]);
3820 }
3821 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
3822 {
3823 self.advance(); let _name = self.expect_ident_like()?;
3825 self.advance(); if !matches!(self.peek(), Token::LParen) {
3827 return Err(self.err(alloc::format!(
3828 "expected '(' after CHECK, got {:?}", self.peek()
3829 )));
3830 }
3831 self.advance();
3832 let expr = self.parse_expr(0)?;
3833 if matches!(self.peek(), Token::RParen) {
3834 self.advance();
3835 }
3836 return Ok(alloc::vec![
3837 crate::ast::AlterTableTarget::AddTableConstraint(
3838 crate::ast::TableConstraint::Check { name: None, expr }
3839 )
3840 ]);
3841 }
3842 }
3845 let is_fk = matches!(
3846 self.peek(),
3847 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3848 || s.eq_ignore_ascii_case("foreign")
3849 );
3850 if is_fk {
3851 let fk = self.parse_table_level_fk()?;
3852 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
3853 }
3854 match self.peek().clone() {
3857 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
3858 self.advance();
3859 self.expect_keyword_ident("key")?;
3860 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3861 return Ok(alloc::vec![
3862 crate::ast::AlterTableTarget::AddTableConstraint(
3863 crate::ast::TableConstraint::PrimaryKey {
3864 name: None,
3865 columns: cols,
3866 }
3867 )
3868 ]);
3869 }
3870 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
3871 self.advance();
3872 let cols = self.parse_paren_ident_list("UNIQUE")?;
3873 return Ok(alloc::vec![
3874 crate::ast::AlterTableTarget::AddTableConstraint(
3875 crate::ast::TableConstraint::Unique {
3876 name: None,
3877 columns: cols,
3878 nulls_not_distinct: false,
3879 }
3880 )
3881 ]);
3882 }
3883 _ => {}
3884 }
3885 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
3886 self.advance();
3887 }
3888 let mut if_not_exists = false;
3889 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3890 self.advance();
3891 if !matches!(self.peek(), Token::Not) {
3892 return Err(self.err(alloc::format!(
3893 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
3894 self.peek()
3895 )));
3896 }
3897 self.advance();
3898 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
3899 return Err(self.err(alloc::format!(
3900 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
3901 self.peek()
3902 )));
3903 }
3904 self.advance();
3905 if_not_exists = true;
3906 }
3907 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
3911 let col_name = column.name.clone();
3912 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
3913 column,
3914 if_not_exists,
3915 }];
3916 if let Some(mut fk) = col_level_fk {
3917 if fk.columns.is_empty() {
3918 fk.columns.push(col_name);
3919 }
3920 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
3921 }
3922 Ok(out)
3923 }
3924 Token::Drop => {
3925 self.advance();
3926 let subject = match self.peek() {
3933 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
3934 self.advance();
3935 "constraint"
3936 }
3937 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
3938 self.advance();
3939 "column"
3940 }
3941 Token::Ident(_) | Token::QuotedIdent(_) => "column",
3945 other => {
3946 return Err(self.err(alloc::format!(
3947 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
3948 )));
3949 }
3950 };
3951 let mut if_exists = false;
3952 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3953 let n1 = self.tokens.get(self.pos + 1);
3954 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3955 self.advance();
3956 self.advance();
3957 if_exists = true;
3958 }
3959 }
3960 let name = self.expect_ident_like()?;
3961 let mut cascade = false;
3962 if matches!(
3963 self.peek(),
3964 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
3965 || s.eq_ignore_ascii_case("restrict")
3966 ) {
3967 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
3968 {
3969 cascade = true;
3970 }
3971 self.advance();
3972 }
3973 if subject == "constraint" {
3974 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
3975 name,
3976 if_exists,
3977 }])
3978 } else {
3979 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
3980 column: name,
3981 if_exists,
3982 cascade,
3983 }])
3984 }
3985 }
3986 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
3987 self.advance();
3988 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
3989 self.advance();
3990 }
3991 let col_name = self.expect_ident_like()?;
3992 match self.peek() {
3993 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
3994 self.advance();
3995 }
3996 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4004 self.consume_until_statement_boundary();
4009 return Ok(Vec::new());
4010 }
4011 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4012 self.consume_until_statement_boundary();
4014 return Ok(Vec::new());
4015 }
4016 other => {
4017 return Err(self.err(alloc::format!(
4018 "expected TYPE / SET / DROP after ALTER COLUMN <name>, got {other:?}"
4019 )));
4020 }
4021 }
4022 let new_type = self.parse_column_type_name()?;
4023 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4024 {
4025 self.advance();
4026 Some(self.parse_expr(0)?)
4027 } else {
4028 None
4029 };
4030 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4031 column: col_name,
4032 new_type,
4033 using,
4034 }])
4035 }
4036 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4043 self.advance();
4044 if matches!(self.peek(), Token::To)
4049 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4050 {
4051 self.advance();
4052 let new = self.expect_ident_like()?;
4053 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4054 new,
4055 }]);
4056 }
4057 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4058 self.advance();
4059 }
4060 let old = self.expect_ident_like()?;
4061 if matches!(self.peek(), Token::To) {
4064 self.advance();
4065 } else {
4066 self.expect_keyword_ident("to")?;
4067 }
4068 let new = self.expect_ident_like()?;
4069 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4070 old,
4071 new,
4072 }])
4073 }
4074 Token::Ident(s)
4081 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4082 {
4083 let enabled = s.eq_ignore_ascii_case("enable");
4084 self.advance();
4085 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4091 return Err(self.err(alloc::format!(
4092 "expected TRIGGER after {}, got {:?}",
4093 if enabled { "ENABLE" } else { "DISABLE" },
4094 self.peek()
4095 )));
4096 }
4097 self.advance();
4098 let which = if matches!(self.peek(), Token::All)
4101 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4102 {
4103 self.advance();
4104 crate::ast::TriggerSelector::All
4105 } else {
4106 let name = self.expect_ident_like()?;
4107 crate::ast::TriggerSelector::Named(name)
4108 };
4109 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4110 which,
4111 enabled,
4112 }])
4113 }
4114 other => Err(self.err(alloc::format!(
4115 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4116 ))),
4117 }
4118 }
4119
4120 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4129 let schema = match self.tokens.get(self.pos) {
4131 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4132 _ => return None,
4133 };
4134 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4136 return None;
4137 }
4138 let tbl = match self.tokens.get(self.pos + 2)? {
4142 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4143 Token::Tables => "tables".to_string(),
4144 _ => return None,
4147 };
4148 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4152 ("__spg_info_", tbl.to_ascii_lowercase())
4153 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4154 let bare = tbl
4155 .to_ascii_lowercase()
4156 .strip_prefix("pg_")
4157 .map(alloc::string::String::from)
4158 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4159 ("__spg_pg_", bare)
4160 } else if schema.eq_ignore_ascii_case("mysql") {
4161 ("__spg_mysql_", tbl.to_ascii_lowercase())
4165 } else {
4166 return None;
4167 };
4168 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4172 }
4173
4174 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4176 match self.advance() {
4177 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4178 other => Err(ParseError {
4179 message: format!("expected {kw:?}, got {other:?}"),
4180 token_pos: self.pos.saturating_sub(1),
4181 }),
4182 }
4183 }
4184
4185 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4189 match self.advance() {
4190 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4191 other => Err(ParseError {
4192 message: format!("expected identifier or string, got {other:?}"),
4193 token_pos: self.pos.saturating_sub(1),
4194 }),
4195 }
4196 }
4197
4198 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4199 match self.advance() {
4200 Token::String(s) => Ok(s),
4201 other => Err(ParseError {
4202 message: format!("expected quoted string, got {other:?}"),
4203 token_pos: self.pos.saturating_sub(1),
4204 }),
4205 }
4206 }
4207
4208 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4209 let mut head = self.parse_bare_select()?;
4214 while matches!(self.peek(), Token::Union) {
4215 self.advance();
4216 let kind = if matches!(self.peek(), Token::All) {
4217 self.advance();
4218 UnionKind::All
4219 } else {
4220 UnionKind::Distinct
4221 };
4222 let peer = self.parse_bare_select()?;
4223 head.unions.push((kind, peer));
4224 }
4225 head.order_by = if matches!(self.peek(), Token::Order) {
4226 self.advance();
4227 if !matches!(self.peek(), Token::By) {
4228 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4229 }
4230 self.advance();
4231 let mut keys = Vec::new();
4234 loop {
4235 let expr = self.parse_expr(0)?;
4236 let desc = if matches!(self.peek(), Token::Desc) {
4237 self.advance();
4238 true
4239 } else if matches!(self.peek(), Token::Asc) {
4240 self.advance();
4241 false
4242 } else {
4243 false
4244 };
4245 keys.push(OrderBy { expr, desc });
4246 if matches!(self.peek(), Token::Comma) {
4247 self.advance();
4248 } else {
4249 break;
4250 }
4251 }
4252 keys
4253 } else {
4254 Vec::new()
4255 };
4256 head.limit = if matches!(self.peek(), Token::Limit) {
4257 self.advance();
4258 if self.consume_limit_unbounded_sentinel() {
4265 None
4266 } else {
4267 Some(self.parse_limit_expr("LIMIT")?)
4268 }
4269 } else {
4270 None
4271 };
4272 head.offset = if matches!(self.peek(), Token::Offset) {
4273 self.advance();
4274 let off = self.parse_limit_expr("OFFSET")?;
4278 self.consume_optional_rows_keyword();
4279 Some(off)
4280 } else {
4281 None
4282 };
4283 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4289 {
4290 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4293 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4294 {
4295 self.advance();
4296 }
4297 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4300 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4301 {
4302 crate::ast::LimitExpr::Literal(1)
4304 } else {
4305 self.parse_limit_expr("FETCH FIRST")?
4306 };
4307 self.consume_optional_rows_keyword();
4309 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4315 if s.eq_ignore_ascii_case("only"))
4316 {
4317 self.advance();
4318 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4319 if s.eq_ignore_ascii_case("with"))
4320 {
4321 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4323 if s.eq_ignore_ascii_case("ties"))
4324 {
4325 self.advance();
4326 head.limit_with_ties = true;
4327 }
4328 }
4329 head.limit = Some(count);
4330 }
4331 self.consume_optional_for_lock_clauses();
4346 Ok(Statement::Select(head))
4347 }
4348
4349 fn consume_optional_for_lock_clauses(&mut self) {
4356 while matches!(self.peek(), Token::For) {
4357 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4361 if s.eq_ignore_ascii_case("no"))
4362 {
4363 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4367 if s.eq_ignore_ascii_case("key"))
4368 {
4369 self.advance(); }
4371 }
4372 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4374 if s.eq_ignore_ascii_case("key"))
4375 {
4376 self.advance(); }
4378 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4384 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4385 {
4386 self.advance();
4387 } else {
4388 return;
4393 }
4394 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4397 if s.eq_ignore_ascii_case("of"))
4398 {
4399 self.advance(); #[allow(clippy::while_let_loop)]
4401 loop {
4402 match self.peek() {
4403 Token::Ident(_) | Token::QuotedIdent(_) => {
4404 self.advance();
4405 if matches!(self.peek(), Token::Dot) {
4407 self.advance();
4408 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4409 self.advance();
4410 }
4411 }
4412 }
4413 _ => break,
4414 }
4415 if matches!(self.peek(), Token::Comma) {
4416 self.advance();
4417 } else {
4418 break;
4419 }
4420 }
4421 }
4422 match self.peek().clone() {
4424 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4425 self.advance();
4426 }
4427 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4428 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4430 if s.eq_ignore_ascii_case("locked"))
4431 {
4432 self.advance(); }
4434 }
4435 _ => {}
4436 }
4437 }
4439 }
4440
4441 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4450 if matches!(self.peek(), Token::Null) {
4451 self.advance();
4452 return true;
4453 }
4454 if matches!(self.peek(), Token::All) {
4455 self.advance();
4456 return true;
4457 }
4458 false
4459 }
4460
4461 fn consume_optional_rows_keyword(&mut self) {
4465 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4466 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4467 {
4468 self.advance();
4469 }
4470 }
4471
4472 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4473 match self.advance() {
4474 Token::Integer(n) if n >= 0 => u32::try_from(n)
4475 .map(crate::ast::LimitExpr::Literal)
4476 .map_err(|_| ParseError {
4477 message: alloc::format!("{label} value too large: {n}"),
4478 token_pos: self.pos.saturating_sub(1),
4479 }),
4480 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4481 other => Err(ParseError {
4482 message: alloc::format!(
4483 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4484 ),
4485 token_pos: self.pos.saturating_sub(1),
4486 }),
4487 }
4488 }
4489
4490 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4495 if !matches!(self.peek(), Token::Select) {
4496 return Err(self.err(format!(
4497 "expected SELECT to start a query block, got {:?}",
4498 self.peek()
4499 )));
4500 }
4501 self.advance();
4502 let distinct = if matches!(self.peek(), Token::Distinct) {
4503 self.advance();
4504 true
4505 } else {
4506 false
4507 };
4508 let items = self.parse_select_list()?;
4509 let from = if matches!(self.peek(), Token::From) {
4510 self.advance();
4511 Some(self.parse_from_clause()?)
4512 } else {
4513 None
4514 };
4515 let where_ = if matches!(self.peek(), Token::Where) {
4516 self.advance();
4517 Some(self.parse_expr(0)?)
4518 } else {
4519 None
4520 };
4521 let mut group_by_all = false;
4522 let group_by = if matches!(self.peek(), Token::Group) {
4523 self.advance();
4524 if !matches!(self.peek(), Token::By) {
4525 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4526 }
4527 self.advance();
4528 if matches!(self.peek(), Token::All) {
4531 self.advance();
4532 group_by_all = true;
4533 None
4534 } else {
4535 let mut groups = Vec::new();
4536 loop {
4537 groups.push(self.parse_expr(0)?);
4538 if matches!(self.peek(), Token::Comma) {
4539 self.advance();
4540 } else {
4541 break;
4542 }
4543 }
4544 Some(groups)
4545 }
4546 } else {
4547 None
4548 };
4549 let having = if matches!(self.peek(), Token::Having) {
4550 self.advance();
4551 Some(self.parse_expr(0)?)
4552 } else {
4553 None
4554 };
4555 Ok(SelectStatement {
4556 ctes: Vec::new(),
4557 distinct,
4558 items,
4559 from,
4560 where_,
4561 group_by,
4562 group_by_all,
4563 having,
4564 unions: Vec::new(),
4565 order_by: Vec::new(),
4566 limit: None,
4567 offset: None,
4568 limit_with_ties: false,
4569 })
4570 }
4571
4572 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4573 debug_assert!(matches!(self.peek(), Token::Table));
4575 self.advance();
4576 let if_not_exists = self.consume_if_not_exists();
4577 let name = self.expect_ident_like()?;
4578 if !matches!(self.peek(), Token::LParen) {
4579 return Err(self.err(format!(
4580 "expected '(' after table name, got {:?}",
4581 self.peek()
4582 )));
4583 }
4584 self.advance();
4585 let mut columns = Vec::new();
4586 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4587 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4588 loop {
4589 if self.peek_table_level_pk_start() {
4595 table_constraints.push(self.parse_table_level_primary_key()?);
4596 } else if self.peek_table_level_unique_start() {
4597 table_constraints.push(self.parse_table_level_unique()?);
4598 } else if self.peek_table_level_check_start() {
4599 table_constraints.push(self.parse_table_level_check()?);
4601 } else if self.peek_mysql_inline_key_start() {
4602 if let Some(uc) = self.parse_mysql_inline_key()? {
4608 table_constraints.push(uc);
4609 }
4610 } else if self.peek_constraint_or_fk_start() {
4611 foreign_keys.push(self.parse_table_level_fk()?);
4612 } else {
4613 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
4614 if col.is_unique {
4618 table_constraints.push(crate::ast::TableConstraint::Unique {
4619 name: None,
4620 columns: alloc::vec![col.name.clone()],
4621 nulls_not_distinct: false,
4622 });
4623 }
4624 if let Some(check_expr) = col.check.clone() {
4625 table_constraints.push(crate::ast::TableConstraint::Check {
4626 name: None,
4627 expr: check_expr,
4628 });
4629 }
4630 columns.push(col);
4631 if let Some(fk) = col_level_fk {
4632 foreign_keys.push(fk);
4633 }
4634 }
4635 match self.peek() {
4636 Token::Comma => {
4637 self.advance();
4638 }
4639 Token::RParen => {
4640 self.advance();
4641 break;
4642 }
4643 other => {
4644 return Err(
4645 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
4646 );
4647 }
4648 }
4649 }
4650 if columns.is_empty() {
4651 return Err(self.err("CREATE TABLE requires at least one column".into()));
4652 }
4653 self.consume_mysql_table_options();
4660 Ok(Statement::CreateTable(CreateTableStatement {
4661 name,
4662 columns,
4663 if_not_exists,
4664 foreign_keys,
4665 table_constraints,
4666 }))
4667 }
4668
4669 fn peek_mysql_inline_key_start(&self) -> bool {
4678 let cur = self.peek();
4679 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
4689 match self.tokens.get(skip) {
4692 Some(Token::LParen) => true,
4693 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
4694 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
4695 }
4696 _ => false,
4697 }
4698 };
4699 let is_key_or_index_tok = |t: &Token| -> bool {
4703 matches!(t, Token::Index)
4704 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
4705 };
4706 match cur {
4707 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
4708 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4709 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
4710 }
4711 Token::Ident(s)
4712 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
4713 {
4714 let nxt = self.tokens.get(self.pos + 1);
4715 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
4716 self.pos + 2
4717 } else {
4718 self.pos + 1
4719 };
4720 after_keyword_followed_by_paren_or_ident_paren(after_after)
4721 }
4722 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4723 let nxt = self.tokens.get(self.pos + 1);
4724 if !nxt.is_some_and(is_key_or_index_tok) {
4725 return false;
4726 }
4727 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
4728 }
4729 _ => false,
4730 }
4731 }
4732
4733 fn parse_mysql_inline_key(
4742 &mut self,
4743 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
4744 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
4746 {
4747 self.advance();
4748 true
4749 } else {
4750 false
4751 };
4752 let mut is_fulltext = false;
4758 let mut is_spatial = false;
4759 if let Token::Ident(s) = self.peek().clone() {
4760 if s.eq_ignore_ascii_case("fulltext") {
4761 self.advance();
4762 is_fulltext = true;
4763 } else if s.eq_ignore_ascii_case("spatial") {
4764 self.advance();
4765 is_spatial = true;
4766 }
4767 }
4768 match self.peek() {
4771 Token::Index => {
4772 self.advance();
4773 }
4774 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4775 self.advance();
4776 }
4777 other => {
4778 return Err(self.err(alloc::format!(
4779 "expected KEY/INDEX in inline index declaration, got {other:?}"
4780 )));
4781 }
4782 }
4783 let mut idx_name: Option<String> = None;
4788 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
4789 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4790 {
4791 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
4792 idx_name = Some(s);
4793 }
4794 }
4795 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4797 self.advance();
4798 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4799 self.advance();
4800 }
4801 }
4802 if !matches!(self.peek(), Token::LParen) {
4804 return Err(self.err(alloc::format!(
4805 "expected '(' in inline KEY/INDEX, got {:?}",
4806 self.peek()
4807 )));
4808 }
4809 self.advance();
4810 let mut cols: Vec<String> = Vec::new();
4811 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
4812 self.advance();
4813 cols.push(s);
4814 if matches!(self.peek(), Token::LParen) {
4816 let mut depth = 1usize;
4817 self.advance();
4818 while depth > 0 {
4819 match self.peek() {
4820 Token::LParen => depth += 1,
4821 Token::RParen => depth -= 1,
4822 Token::Eof => break,
4823 _ => {}
4824 }
4825 self.advance();
4826 }
4827 }
4828 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
4830 || matches!(self.peek(), Token::Asc | Token::Desc)
4831 {
4832 self.advance();
4833 }
4834 if matches!(self.peek(), Token::Comma) {
4835 self.advance();
4836 continue;
4837 }
4838 break;
4839 }
4840 if matches!(self.peek(), Token::RParen) {
4841 self.advance();
4842 }
4843 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
4846 self.advance();
4847 }
4848 if cols.is_empty() {
4849 return Ok(None);
4850 }
4851 if is_unique {
4852 Ok(Some(crate::ast::TableConstraint::Unique {
4858 name: idx_name,
4859 columns: cols,
4860 nulls_not_distinct: false,
4861 }))
4862 } else if is_fulltext {
4863 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
4869 name: idx_name,
4870 columns: cols,
4871 }))
4872 } else if is_spatial {
4873 Ok(None)
4876 } else {
4877 Ok(Some(crate::ast::TableConstraint::Index {
4880 name: idx_name,
4881 columns: cols,
4882 }))
4883 }
4884 }
4885
4886 fn consume_mysql_table_options(&mut self) {
4891 loop {
4892 let name_lc = match self.peek().clone() {
4896 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
4897 Token::Default => alloc::string::String::from("default"),
4898 _ => break,
4899 };
4900 let known = matches!(
4901 name_lc.as_str(),
4902 "engine"
4903 | "default"
4904 | "charset"
4905 | "collate"
4906 | "auto_increment"
4907 | "row_format"
4908 | "comment"
4909 | "pack_keys"
4910 | "stats_persistent"
4911 | "stats_auto_recalc"
4912 | "stats_sample_pages"
4913 | "key_block_size"
4914 | "tablespace"
4915 | "min_rows"
4916 | "max_rows"
4917 | "checksum"
4918 | "delay_key_write"
4919 | "insert_method"
4920 | "data"
4921 | "index"
4922 | "encryption"
4923 | "compression"
4924 );
4925 if !known {
4926 break;
4927 }
4928 self.advance(); if name_lc == "default" {
4932 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4933 self.advance();
4934 }
4935 }
4936 if matches!(self.peek(), Token::Eq) {
4937 self.advance();
4938 }
4939 match self.peek() {
4940 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
4941 self.advance();
4942 }
4943 _ => {}
4944 }
4945 }
4946 }
4947
4948 fn peek_table_level_pk_start(&self) -> bool {
4953 let cur = self.peek();
4954 let nxt = self.tokens.get(self.pos + 1);
4955 let nxt2 = self.tokens.get(self.pos + 2);
4956 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
4957 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
4958 let is_lparen = matches!(nxt2, Some(Token::LParen));
4959 is_primary && is_key && is_lparen
4960 }
4961
4962 fn peek_table_level_unique_start(&self) -> bool {
4966 let cur = self.peek();
4967 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
4968 if !is_unique {
4969 return false;
4970 }
4971 let n1 = self.tokens.get(self.pos + 1);
4972 if matches!(n1, Some(Token::LParen)) {
4974 return true;
4975 }
4976 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
4978 if !is_nulls {
4979 return false;
4980 }
4981 let n2 = self.tokens.get(self.pos + 2);
4982 let n3 = self.tokens.get(self.pos + 3);
4983 let n4 = self.tokens.get(self.pos + 4);
4984 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
4986 return true;
4987 }
4988 if matches!(n2, Some(Token::Not))
4990 && matches!(n3, Some(Token::Distinct))
4991 && matches!(n4, Some(Token::LParen))
4992 {
4993 return true;
4994 }
4995 false
4996 }
4997
4998 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
4999 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5002 Ok(crate::ast::TableConstraint::PrimaryKey {
5003 name: None,
5004 columns,
5005 })
5006 }
5007
5008 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5009 self.advance(); let mut nulls_not_distinct = false;
5014 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5015 let n1 = self.tokens.get(self.pos + 1);
5016 let n2 = self.tokens.get(self.pos + 2);
5017 let is_not = matches!(n1, Some(Token::Not));
5018 let is_distinct = matches!(n2, Some(Token::Distinct));
5019 if is_not && is_distinct {
5020 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5024 } else if matches!(n1, Some(Token::Distinct)) {
5025 self.advance(); self.advance(); }
5028 }
5029 let columns = self.parse_paren_ident_list("UNIQUE")?;
5030 Ok(crate::ast::TableConstraint::Unique {
5031 name: None,
5032 columns,
5033 nulls_not_distinct,
5034 })
5035 }
5036
5037 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5041 self.advance(); if !matches!(self.peek(), Token::LParen) {
5043 return Err(self.err(alloc::format!(
5044 "expected '(' after CHECK, got {:?}",
5045 self.peek()
5046 )));
5047 }
5048 self.advance();
5049 let expr = self.parse_expr(0)?;
5050 if !matches!(self.peek(), Token::RParen) {
5051 return Err(self.err(alloc::format!(
5052 "expected ')' to close CHECK predicate, got {:?}",
5053 self.peek()
5054 )));
5055 }
5056 self.advance();
5057 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5058 }
5059
5060 fn peek_table_level_check_start(&self) -> bool {
5062 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5063 }
5064
5065 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5066 if !matches!(self.peek(), Token::LParen) {
5067 return Err(self.err(alloc::format!(
5068 "expected '(' after {ctx}, got {:?}",
5069 self.peek()
5070 )));
5071 }
5072 self.advance();
5073 let mut out = Vec::new();
5074 loop {
5075 out.push(self.expect_ident_like()?);
5076 match self.peek() {
5077 Token::Comma => {
5078 self.advance();
5079 }
5080 Token::RParen => {
5081 self.advance();
5082 break;
5083 }
5084 other => {
5085 return Err(self.err(alloc::format!(
5086 "expected ',' or ')' in {ctx} list, got {other:?}"
5087 )));
5088 }
5089 }
5090 }
5091 if out.is_empty() {
5092 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5093 }
5094 Ok(out)
5095 }
5096
5097 fn peek_constraint_or_fk_start(&self) -> bool {
5102 let is_constraint_kw = matches!(
5103 self.peek(),
5104 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5105 );
5106 let is_foreign_kw = matches!(
5107 self.peek(),
5108 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5109 );
5110 is_constraint_kw || is_foreign_kw
5111 }
5112
5113 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5117 let mut name: Option<String> = None;
5118 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5119 self.advance();
5120 name = Some(self.expect_ident_like()?);
5121 }
5122 match self.advance() {
5124 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5125 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5126 }
5127 match self.advance() {
5129 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5130 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5131 }
5132 if !matches!(self.peek(), Token::LParen) {
5134 return Err(self.err(format!(
5135 "expected '(' after FOREIGN KEY, got {:?}",
5136 self.peek()
5137 )));
5138 }
5139 self.advance();
5140 let mut columns = Vec::new();
5141 loop {
5142 columns.push(self.expect_ident_like()?);
5143 match self.peek() {
5144 Token::Comma => {
5145 self.advance();
5146 }
5147 Token::RParen => {
5148 self.advance();
5149 break;
5150 }
5151 other => {
5152 return Err(self.err(format!(
5153 "expected ',' or ')' in FK column list, got {other:?}"
5154 )));
5155 }
5156 }
5157 }
5158 if columns.is_empty() {
5159 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5160 }
5161 let (parent_table, parent_columns, on_delete, on_update) =
5162 self.parse_references_tail(columns.len())?;
5163 Ok(ForeignKeyConstraint {
5164 name,
5165 columns,
5166 parent_table,
5167 parent_columns,
5168 on_delete,
5169 on_update,
5170 })
5171 }
5172
5173 fn parse_references_tail(
5178 &mut self,
5179 expected_arity: usize,
5180 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5181 match self.advance() {
5182 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5183 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5184 }
5185 let parent_table = self.expect_ident_like()?;
5186 let mut parent_columns: Vec<String> = Vec::new();
5187 if matches!(self.peek(), Token::LParen) {
5188 self.advance();
5189 loop {
5190 parent_columns.push(self.expect_ident_like()?);
5191 match self.peek() {
5192 Token::Comma => {
5193 self.advance();
5194 }
5195 Token::RParen => {
5196 self.advance();
5197 break;
5198 }
5199 other => {
5200 return Err(self.err(format!(
5201 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5202 )));
5203 }
5204 }
5205 }
5206 }
5207 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5208 return Err(self.err(format!(
5209 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5210 expected_arity,
5211 parent_columns.len()
5212 )));
5213 }
5214 let mut on_delete = FkAction::Restrict;
5227 let mut on_update = FkAction::Restrict;
5228 let mut seen_on_delete = false;
5229 let mut seen_on_update = false;
5230 loop {
5231 let before = self.pos;
5233 self.consume_optional_deferrable_clauses()?;
5234 if self.pos != before {
5235 continue;
5236 }
5237 if !matches!(self.peek(), Token::On) {
5239 break;
5240 }
5241 self.advance();
5242 let which = self.advance();
5243 let action = self.parse_fk_action()?;
5244 match which {
5245 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5246 if seen_on_delete {
5247 return Err(self.err("ON DELETE specified twice".into()));
5248 }
5249 seen_on_delete = true;
5250 on_delete = action;
5251 }
5252 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5253 if seen_on_update {
5254 return Err(self.err("ON UPDATE specified twice".into()));
5255 }
5256 seen_on_update = true;
5257 on_update = action;
5258 }
5259 other => {
5260 return Err(
5261 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5262 );
5263 }
5264 }
5265 }
5266 Ok((parent_table, parent_columns, on_delete, on_update))
5267 }
5268
5269 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5272 match self.advance() {
5273 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5274 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5275 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5276 Token::Null => Ok(FkAction::SetNull),
5277 Token::Default => Ok(FkAction::SetDefault),
5278 other => Err(self.err(format!(
5279 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5280 ))),
5281 },
5282 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5283 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5284 other => Err(self.err(format!(
5285 "expected ACTION after NO in FK action, got {other:?}"
5286 ))),
5287 },
5288 other => Err(self.err(format!(
5289 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5290 ))),
5291 }
5292 }
5293
5294 fn consume_if_not_exists(&mut self) -> bool {
5297 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5301 if !looks_like_if {
5302 return false;
5303 }
5304 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5307 return false;
5308 }
5309 if !matches!(
5310 self.tokens.get(self.pos + 2),
5311 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5312 ) {
5313 return false;
5314 }
5315 self.advance(); self.advance(); self.advance(); true
5319 }
5320
5321 fn consume_if_exists(&mut self) -> bool {
5325 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5326 if !looks_like_if {
5327 return false;
5328 }
5329 if !matches!(
5330 self.tokens.get(self.pos + 1),
5331 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5332 ) {
5333 return false;
5334 }
5335 self.advance(); self.advance(); true
5338 }
5339
5340 fn consume_optional_index_column_qualifiers(&mut self) {
5346 loop {
5347 match self.peek() {
5348 Token::Asc | Token::Desc => {
5349 self.advance();
5350 }
5351 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5352 let look = self.tokens.get(self.pos + 1);
5353 if matches!(
5354 look,
5355 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5356 || k.eq_ignore_ascii_case("last")
5357 ) {
5358 self.advance();
5359 self.advance();
5360 } else {
5361 break;
5362 }
5363 }
5364 _ => break,
5365 }
5366 }
5367 }
5368
5369 fn parse_create_index_stmt_after_create(
5370 &mut self,
5371 is_unique: bool,
5372 ) -> Result<Statement, ParseError> {
5373 debug_assert!(matches!(self.peek(), Token::Index));
5375 self.advance();
5376 let if_not_exists = self.consume_if_not_exists();
5377 let name = self.expect_ident_like()?;
5378 if !matches!(self.peek(), Token::On) {
5379 return Err(self.err(format!(
5380 "expected ON after CREATE INDEX <name>, got {:?}",
5381 self.peek()
5382 )));
5383 }
5384 self.advance();
5385 let table = self.expect_ident_like()?;
5386 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5391 self.advance();
5392 let m = self.expect_ident_like()?;
5393 match m.to_ascii_lowercase().as_str() {
5394 "hnsw" => IndexMethod::Hnsw,
5395 "btree" => IndexMethod::BTree,
5396 "brin" => IndexMethod::Brin,
5397 "gin" => IndexMethod::Gin,
5402 "gist" | "spgist" | "hash" => IndexMethod::BTree,
5411 "ivfflat" => IndexMethod::Hnsw,
5419 other => {
5420 return Err(self.err(alloc::format!(
5421 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
5422 )));
5423 }
5424 }
5425 } else {
5426 IndexMethod::BTree
5427 };
5428 if !matches!(self.peek(), Token::LParen) {
5429 return Err(self.err(format!(
5430 "expected '(' before indexed column, got {:?}",
5431 self.peek()
5432 )));
5433 }
5434 self.advance();
5435 let mut opclass: Option<String> = None;
5444 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
5445 Token::Ident(s) | Token::QuotedIdent(s)
5452 if matches!(
5453 self.tokens.get(self.pos + 1),
5454 Some(Token::RParen | Token::Comma)
5455 ) =>
5456 {
5457 self.advance();
5458 (s, None)
5459 }
5460 Token::Ident(s) | Token::QuotedIdent(s)
5470 if matches!(
5471 self.tokens.get(self.pos + 1),
5472 Some(Token::Ident(op) | Token::QuotedIdent(op))
5473 if is_vector_opclass_name(op)
5474 ) =>
5475 {
5476 self.advance(); let op_tok = self.advance();
5480 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5481 opclass = Some(op.to_ascii_lowercase());
5482 }
5483 (s, None)
5484 }
5485 Token::Ident(_) | Token::QuotedIdent(_) => {
5486 let key_expr = self.parse_expr(0)?;
5487 let primary = extract_first_column(&key_expr).ok_or_else(|| {
5488 self.err("expression index key must reference at least one column".into())
5489 })?;
5490 (primary, Some(key_expr))
5491 }
5492 other => {
5493 return Err(self.err(format!(
5494 "expected column ident or expression, got {other:?}"
5495 )));
5496 }
5497 };
5498 let mut extra_columns: Vec<String> = Vec::new();
5507 self.consume_optional_index_column_qualifiers();
5509 while matches!(self.peek(), Token::Comma) {
5510 self.advance();
5511 let extra = self.expect_ident_like()?;
5512 self.consume_optional_index_column_qualifiers();
5513 extra_columns.push(extra);
5514 }
5515 if !matches!(self.peek(), Token::RParen) {
5516 return Err(self.err(format!(
5517 "expected ')' after indexed column / expression, got {:?}",
5518 self.peek()
5519 )));
5520 }
5521 self.advance();
5522 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
5526 {
5527 self.advance();
5528 if !matches!(self.peek(), Token::LParen) {
5529 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
5530 }
5531 self.advance();
5532 let mut cols = Vec::new();
5533 loop {
5534 cols.push(self.expect_ident_like()?);
5535 match self.peek() {
5536 Token::Comma => {
5537 self.advance();
5538 }
5539 Token::RParen => {
5540 self.advance();
5541 break;
5542 }
5543 other => {
5544 return Err(self.err(format!(
5545 "expected ',' or ')' in INCLUDE list, got {other:?}"
5546 )));
5547 }
5548 }
5549 }
5550 cols
5551 } else {
5552 Vec::new()
5553 };
5554 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
5560 self.advance();
5561 if !matches!(self.peek(), Token::LParen) {
5562 return Err(self.err(format!(
5563 "expected '(' after WITH in CREATE INDEX, got {:?}",
5564 self.peek()
5565 )));
5566 }
5567 self.advance();
5568 loop {
5569 if matches!(self.peek(), Token::RParen) {
5570 self.advance();
5571 break;
5572 }
5573 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
5576 self.advance();
5577 let _ = self.advance(); }
5579 match self.peek() {
5580 Token::Comma => {
5581 self.advance();
5582 }
5583 Token::RParen => {
5584 self.advance();
5585 break;
5586 }
5587 other => {
5588 return Err(self.err(format!(
5589 "expected ',' or ')' in WITH (…) clause, got {other:?}"
5590 )));
5591 }
5592 }
5593 }
5594 }
5595 let partial_predicate = if matches!(self.peek(), Token::Where) {
5597 self.advance();
5598 Some(self.parse_expr(0)?)
5599 } else {
5600 None
5601 };
5602 if is_unique && !matches!(method, IndexMethod::BTree) {
5607 return Err(self.err(alloc::format!(
5608 "UNIQUE is only supported on BTree indexes, got USING {:?}",
5609 method
5610 )));
5611 }
5612 Ok(Statement::CreateIndex(CreateIndexStatement {
5613 name,
5614 table,
5615 column,
5616 method,
5617 if_not_exists,
5618 included_columns,
5619 partial_predicate,
5620 extra_columns: extra_columns.clone(),
5621 expression,
5622 is_unique,
5623 opclass,
5624 }))
5625 }
5626
5627 fn parse_column_def_with_fk(
5632 &mut self,
5633 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
5634 let col = self.parse_column_def()?;
5635 let inline_references = matches!(
5637 self.peek(),
5638 Token::Ident(s) if s.eq_ignore_ascii_case("references")
5639 );
5640 if !inline_references {
5641 return Ok((col, None));
5642 }
5643 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
5644 let fk = ForeignKeyConstraint {
5645 name: None,
5646 columns: vec![col.name.clone()],
5647 parent_table,
5648 parent_columns,
5649 on_delete,
5650 on_update,
5651 };
5652 Ok((col, Some(fk)))
5653 }
5654
5655 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
5663 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
5664 Ok(ty)
5665 }
5666
5667 #[allow(clippy::type_complexity)]
5668 fn parse_type_with_implied_flags(
5669 &mut self,
5670 ) -> Result<
5671 (
5672 ColumnTypeName,
5673 bool,
5674 bool,
5675 Option<String>,
5676 Collation,
5677 bool,
5678 Option<Vec<String>>,
5682 Option<Vec<String>>,
5685 ),
5686 ParseError,
5687 > {
5688 let ty_ident = match self.advance() {
5689 Token::Ident(s) => s,
5690 other => {
5691 return Err(ParseError {
5692 message: format!("expected column type, got {other:?}"),
5693 token_pos: self.pos.saturating_sub(1),
5694 });
5695 }
5696 };
5697 let mut implied_auto_increment = false;
5698 let mut implied_not_null = false;
5699 let mut user_type_ref: Option<String> = None;
5700 let mut inline_enum_variants: Option<Vec<String>> = None;
5705 let mut inline_set_variants: Option<Vec<String>> = None;
5707 let mut ty = match ty_ident.as_str() {
5708 "smallserial" | "serial2" => {
5710 implied_auto_increment = true;
5711 implied_not_null = true;
5712 ColumnTypeName::SmallInt
5713 }
5714 "serial" | "serial4" => {
5715 implied_auto_increment = true;
5716 implied_not_null = true;
5717 ColumnTypeName::Int
5718 }
5719 "bigserial" | "serial8" => {
5720 implied_auto_increment = true;
5721 implied_not_null = true;
5722 ColumnTypeName::BigInt
5723 }
5724 "smallint" => {
5730 self.consume_optional_paren_size();
5735 ColumnTypeName::SmallInt
5736 }
5737 "tinyint" => {
5748 let width = self.peek_optional_paren_size_value();
5749 self.consume_optional_paren_size();
5750 if width == Some(1) {
5751 ColumnTypeName::Bool
5752 } else {
5753 ColumnTypeName::SmallInt
5754 }
5755 }
5756 "int" | "integer" | "mediumint" => {
5757 self.consume_optional_paren_size();
5758 ColumnTypeName::Int
5759 }
5760 "bigint" => {
5761 self.consume_optional_paren_size();
5762 ColumnTypeName::BigInt
5763 }
5764 "float" | "double" | "real" => {
5769 if ty_ident.eq_ignore_ascii_case("double")
5770 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
5771 {
5772 self.advance();
5773 }
5774 ColumnTypeName::Float
5775 }
5776 "float4" | "float8" => ColumnTypeName::Float,
5778 "text" => ColumnTypeName::Text,
5779 "bool" | "boolean" => ColumnTypeName::Bool,
5780 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
5781 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
5782 "vector" => {
5783 let dim = self.parse_paren_size("VECTOR")?;
5784 let encoding = self.parse_optional_vector_encoding()?;
5785 ColumnTypeName::Vector { dim, encoding }
5786 }
5787 "numeric" => {
5788 let (precision, scale) = self.parse_optional_numeric_params()?;
5789 ColumnTypeName::Numeric(precision, scale)
5790 }
5791 "date" => ColumnTypeName::Date,
5792 "timestamp" | "datetime" => {
5795 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
5801 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
5802 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
5803 {
5804 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
5808 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
5809 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
5810 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
5811 {
5812 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
5816 } else {
5817 self.consume_optional_paren_size();
5821 ColumnTypeName::Timestamp
5822 }
5823 }
5824 "timestamptz" => ColumnTypeName::Timestamptz,
5828 "json" => ColumnTypeName::Json,
5833 "jsonb" => ColumnTypeName::Jsonb,
5834 "bytea" | "bytes" => ColumnTypeName::Bytes,
5840 "inet" | "cidr" | "macaddr" => ColumnTypeName::Text,
5850 "tsvector" => ColumnTypeName::TsVector,
5855 "tsquery" => ColumnTypeName::TsQuery,
5856 "uuid" => ColumnTypeName::Uuid,
5860 "time" => ColumnTypeName::Time,
5863 "year" => ColumnTypeName::Year,
5866 "timetz" => ColumnTypeName::TimeTz,
5869 "money" => ColumnTypeName::Money,
5872 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
5874 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
5875 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
5876 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
5877 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
5878 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
5879 "hstore" => ColumnTypeName::Hstore,
5881 "enum" => {
5887 if !matches!(self.peek(), Token::LParen) {
5889 return Err(self.err(alloc::format!(
5890 "expected '(' after ENUM, got {:?}",
5891 self.peek()
5892 )));
5893 }
5894 self.advance();
5895 let mut variants: Vec<String> = Vec::new();
5896 loop {
5897 match self.advance() {
5898 Token::String(s) => variants.push(s),
5899 other => {
5900 return Err(self.err(alloc::format!(
5901 "ENUM(...) expects string literal variants, got {other:?}"
5902 )));
5903 }
5904 }
5905 match self.peek() {
5906 Token::Comma => {
5907 self.advance();
5908 continue;
5909 }
5910 Token::RParen => {
5911 self.advance();
5912 break;
5913 }
5914 other => {
5915 return Err(self.err(alloc::format!(
5916 "expected ',' or ')' in ENUM(...), got {other:?}"
5917 )));
5918 }
5919 }
5920 }
5921 if variants.is_empty() {
5922 return Err(self.err("ENUM(...) must declare at least one variant".into()));
5923 }
5924 inline_enum_variants = Some(variants);
5925 ColumnTypeName::Text
5928 }
5929 "set" => {
5933 if !matches!(self.peek(), Token::LParen) {
5934 return Err(self.err(alloc::format!(
5935 "expected '(' after SET, got {:?}",
5936 self.peek()
5937 )));
5938 }
5939 self.advance();
5940 let mut variants: Vec<String> = Vec::new();
5941 loop {
5942 match self.advance() {
5943 Token::String(s) => variants.push(s),
5944 other => {
5945 return Err(self.err(alloc::format!(
5946 "SET(...) expects string literal variants, got {other:?}"
5947 )));
5948 }
5949 }
5950 match self.peek() {
5951 Token::Comma => {
5952 self.advance();
5953 continue;
5954 }
5955 Token::RParen => {
5956 self.advance();
5957 break;
5958 }
5959 other => {
5960 return Err(self.err(alloc::format!(
5961 "expected ',' or ')' in SET(...), got {other:?}"
5962 )));
5963 }
5964 }
5965 }
5966 if variants.is_empty() {
5967 return Err(self.err("SET(...) must declare at least one variant".into()));
5968 }
5969 inline_set_variants = Some(variants);
5970 ColumnTypeName::Text
5971 }
5972 _other => {
5973 user_type_ref = Some(ty_ident.clone());
5979 ColumnTypeName::Text
5980 }
5981 };
5982 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
5992 {
5993 self.advance();
5994 true
5995 } else {
5996 false
5997 };
5998 let mut collation = Collation::Binary;
6014 loop {
6015 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6016 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6017 {
6018 self.advance(); self.advance(); if matches!(
6021 self.peek(),
6022 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6023 ) {
6024 self.advance();
6025 }
6026 continue;
6027 }
6028 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6029 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6036 match this.peek().clone() {
6037 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6038 this.advance();
6039 Some(s)
6040 }
6041 Token::Default => {
6042 this.advance();
6043 Some(alloc::string::String::from("default"))
6044 }
6045 _ => None,
6046 }
6047 };
6048 let raw = if let Some(head) = read_collation_atom(self) {
6049 if matches!(self.peek(), Token::Dot) {
6051 self.advance();
6052 let tail = read_collation_atom(self).unwrap_or_default();
6053 alloc::format!("{head}.{tail}")
6054 } else {
6055 head
6056 }
6057 } else {
6058 alloc::string::String::new()
6059 };
6060 if !raw.is_empty() {
6061 let parsed = Collation::from_collation_name(&raw);
6062 if parsed != Collation::Binary {
6068 collation = parsed;
6069 }
6070 }
6071 continue;
6072 }
6073 break;
6074 }
6075 if matches!(self.peek(), Token::LBracket) {
6080 self.advance();
6081 if !matches!(self.peek(), Token::RBracket) {
6082 return Err(self.err(alloc::format!(
6083 "TEXT[] takes no dimension; got {:?}",
6084 self.peek()
6085 )));
6086 }
6087 self.advance();
6088 ty = match ty {
6092 ColumnTypeName::Text => ColumnTypeName::TextArray,
6093 ColumnTypeName::Int => ColumnTypeName::IntArray,
6094 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6095 other => {
6096 return Err(self.err(alloc::format!(
6097 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
6098 )));
6099 }
6100 };
6101 if matches!(self.peek(), Token::LBracket) {
6104 self.advance();
6105 if !matches!(self.peek(), Token::RBracket) {
6106 return Err(self.err(alloc::format!(
6107 "TYPE[][] second dimension takes no size; got {:?}",
6108 self.peek()
6109 )));
6110 }
6111 self.advance();
6112 ty = match ty {
6113 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6114 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6115 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6116 other => {
6117 return Err(self.err(alloc::format!(
6118 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6119 TEXT[][] only; got {other:?}"
6120 )));
6121 }
6122 };
6123 }
6124 }
6125 Ok((
6126 ty,
6127 implied_auto_increment,
6128 implied_not_null,
6129 user_type_ref,
6130 collation,
6131 is_unsigned,
6132 inline_enum_variants,
6133 inline_set_variants,
6134 ))
6135 }
6136
6137 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6138 let name = self.expect_ident_like()?;
6139 let (
6140 ty,
6141 implied_auto_increment,
6142 implied_not_null,
6143 user_type_ref,
6144 collation,
6145 is_unsigned,
6146 inline_enum_variants,
6147 inline_set_variants,
6148 ) = self.parse_type_with_implied_flags()?;
6149 let mut default: Option<Expr> = None;
6153 let mut nullable = !implied_not_null;
6154 let mut nullability_seen = implied_not_null;
6155 let mut auto_increment = implied_auto_increment;
6156 let mut is_primary_key = false;
6157 let mut is_unique = false;
6158 let mut check: Option<Expr> = None;
6159 let mut on_update_runtime: Option<Expr> = None;
6160 loop {
6161 if matches!(self.peek(), Token::On)
6166 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6167 {
6168 self.advance(); self.advance(); let next = self.peek().clone();
6172 match next {
6173 Token::Ident(s) | Token::QuotedIdent(s)
6174 if s.eq_ignore_ascii_case("current_timestamp") =>
6175 {
6176 self.advance();
6177 if matches!(self.peek(), Token::LParen) {
6179 self.advance();
6180 if !matches!(self.peek(), Token::Integer(_)) {
6181 return Err(self.err(alloc::format!(
6182 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6183 self.peek()
6184 )));
6185 }
6186 self.advance();
6187 if !matches!(self.peek(), Token::RParen) {
6188 return Err(self.err(alloc::format!(
6189 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6190 self.peek()
6191 )));
6192 }
6193 self.advance();
6194 }
6195 on_update_runtime = Some(Expr::FunctionCall {
6196 name: "now".into(),
6197 args: Vec::new(),
6198 });
6199 continue;
6200 }
6201 other => {
6202 return Err(self.err(alloc::format!(
6203 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6204 )));
6205 }
6206 }
6207 }
6208 if matches!(self.peek(), Token::Default) {
6209 if default.is_some() {
6210 return Err(self.err("DEFAULT specified twice".into()));
6211 }
6212 self.advance();
6213 default = Some(self.parse_expr(0)?);
6214 continue;
6215 }
6216 if matches!(self.peek(), Token::Not) {
6217 if nullability_seen {
6218 return Err(self.err("NOT NULL specified twice".into()));
6219 }
6220 self.advance();
6221 if !matches!(self.peek(), Token::Null) {
6222 return Err(self.err(format!(
6223 "expected NULL after NOT in column def, got {:?}",
6224 self.peek()
6225 )));
6226 }
6227 self.advance();
6228 nullable = false;
6229 nullability_seen = true;
6230 continue;
6231 }
6232 if matches!(self.peek(), Token::Null) {
6238 if nullability_seen && !nullable {
6239 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6240 }
6241 self.advance();
6242 nullable = true;
6243 nullability_seen = true;
6244 continue;
6245 }
6246 if let Token::Ident(s) = self.peek()
6249 && (s.eq_ignore_ascii_case("auto_increment")
6250 || s.eq_ignore_ascii_case("autoincrement"))
6251 {
6252 if auto_increment {
6253 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6254 }
6255 self.advance();
6256 auto_increment = true;
6257 continue;
6258 }
6259 if let Token::Ident(s) = self.peek()
6264 && s.eq_ignore_ascii_case("primary")
6265 {
6266 if is_primary_key {
6267 return Err(self.err("PRIMARY KEY specified twice".into()));
6268 }
6269 let next = self.tokens.get(self.pos + 1);
6271 let next_is_key = matches!(
6272 next,
6273 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6274 );
6275 if !next_is_key {
6276 return Err(self.err(format!(
6277 "expected KEY after PRIMARY in column def, got {:?}",
6278 next
6279 )));
6280 }
6281 self.advance(); self.advance(); is_primary_key = true;
6284 if nullability_seen && nullable {
6285 return Err(self.err(
6286 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6287 ));
6288 }
6289 nullable = false;
6290 nullability_seen = true;
6291 continue;
6292 }
6293 if let Token::Ident(s) = self.peek()
6297 && s.eq_ignore_ascii_case("unique")
6298 {
6299 if is_unique {
6300 return Err(self.err("UNIQUE specified twice".into()));
6301 }
6302 self.advance();
6303 is_unique = true;
6304 continue;
6305 }
6306 if let Token::Ident(s) = self.peek()
6311 && s.eq_ignore_ascii_case("check")
6312 {
6313 self.advance();
6314 if !matches!(self.peek(), Token::LParen) {
6315 return Err(self.err(alloc::format!(
6316 "expected '(' after CHECK in column def, got {:?}",
6317 self.peek()
6318 )));
6319 }
6320 self.advance();
6321 let pred = self.parse_expr(0)?;
6322 if !matches!(self.peek(), Token::RParen) {
6323 return Err(self.err(alloc::format!(
6324 "expected ')' to close CHECK predicate, got {:?}",
6325 self.peek()
6326 )));
6327 }
6328 self.advance();
6329 check = Some(match check.take() {
6330 Some(prev) => Expr::Binary {
6331 op: BinOp::And,
6332 lhs: Box::new(prev),
6333 rhs: Box::new(pred),
6334 },
6335 None => pred,
6336 });
6337 continue;
6338 }
6339 break;
6340 }
6341 Ok(ColumnDef {
6342 name,
6343 ty,
6344 nullable,
6345 default,
6346 auto_increment,
6347 is_primary_key,
6348 is_unique,
6349 check,
6350 user_type_ref,
6351 on_update_runtime,
6352 collation,
6353 is_unsigned,
6354 inline_enum_variants,
6355 inline_set_variants,
6356 })
6357 }
6358
6359 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6363 if !matches!(self.peek(), Token::LParen) {
6364 return Ok((0, 0));
6368 }
6369 self.advance();
6370 let precision = match self.advance() {
6371 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6372 other => {
6373 return Err(ParseError {
6374 message: format!(
6375 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6376 ),
6377 token_pos: self.pos.saturating_sub(1),
6378 });
6379 }
6380 };
6381 let scale = if matches!(self.peek(), Token::Comma) {
6382 self.advance();
6383 match self.advance() {
6384 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6385 u8::try_from(n).expect("range-checked")
6386 }
6387 other => {
6388 return Err(ParseError {
6389 message: format!(
6390 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6391 ),
6392 token_pos: self.pos.saturating_sub(1),
6393 });
6394 }
6395 }
6396 } else {
6397 0
6398 };
6399 if !matches!(self.peek(), Token::RParen) {
6400 return Err(self.err(format!(
6401 "expected ')' to close NUMERIC params, got {:?}",
6402 self.peek()
6403 )));
6404 }
6405 self.advance();
6406 Ok((precision, scale))
6407 }
6408
6409 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6417 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6418 return Ok(VecEncoding::F32);
6419 }
6420 let n1 = self.tokens.get(self.pos + 1);
6426 let next_is_encoding = matches!(
6427 n1,
6428 Some(Token::Ident(s))
6429 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6430 );
6431 if !next_is_encoding {
6432 return Ok(VecEncoding::F32);
6433 }
6434 self.advance();
6435 let enc_ident = match self.advance() {
6436 Token::Ident(s) => s,
6437 other => {
6438 return Err(self.err(format!(
6439 "expected vector encoding after USING, got {other:?}"
6440 )));
6441 }
6442 };
6443 match enc_ident.to_ascii_lowercase().as_str() {
6444 "sq8" => Ok(VecEncoding::Sq8),
6445 "half" => Ok(VecEncoding::F16),
6448 other => Err(self.err(format!(
6449 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6450 ))),
6451 }
6452 }
6453
6454 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6460 if !matches!(self.peek(), Token::LParen) {
6461 return None;
6462 }
6463 let next = self.tokens.get(self.pos + 1)?;
6464 let n = match next {
6465 Token::Integer(n) => *n,
6466 _ => return None,
6467 };
6468 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6469 return None;
6470 }
6471 Some(n)
6472 }
6473
6474 fn consume_optional_paren_size(&mut self) {
6478 if !matches!(self.peek(), Token::LParen) {
6479 return;
6480 }
6481 self.advance();
6482 let mut depth = 1usize;
6484 while depth > 0 {
6485 match self.peek() {
6486 Token::LParen => depth += 1,
6487 Token::RParen => depth -= 1,
6488 Token::Eof => return,
6489 _ => {}
6490 }
6491 self.advance();
6492 }
6493 }
6494
6495 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6496 if !matches!(self.peek(), Token::LParen) {
6497 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6498 }
6499 self.advance();
6500 let n = match self.advance() {
6501 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6502 message: format!("{label} size too large: {n}"),
6503 token_pos: self.pos.saturating_sub(1),
6504 })?,
6505 other => {
6506 return Err(ParseError {
6507 message: format!("expected positive integer {label} size, got {other:?}"),
6508 token_pos: self.pos.saturating_sub(1),
6509 });
6510 }
6511 };
6512 if !matches!(self.peek(), Token::RParen) {
6513 return Err(self.err(format!(
6514 "expected ')' after {label} size, got {:?}",
6515 self.peek()
6516 )));
6517 }
6518 self.advance();
6519 Ok(n)
6520 }
6521
6522 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6523 debug_assert!(matches!(self.peek(), Token::Insert));
6524 self.advance();
6525 if !matches!(self.peek(), Token::Into) {
6526 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6527 }
6528 self.advance();
6529 let table = self.expect_ident_like()?;
6530 let columns = if matches!(self.peek(), Token::LParen) {
6532 self.advance();
6533 let mut names = Vec::new();
6534 loop {
6535 names.push(self.expect_ident_like()?);
6536 match self.peek() {
6537 Token::Comma => {
6538 self.advance();
6539 }
6540 Token::RParen => {
6541 self.advance();
6542 break;
6543 }
6544 other => {
6545 return Err(self.err(format!(
6546 "expected ',' or ')' in INSERT column list, got {other:?}"
6547 )));
6548 }
6549 }
6550 }
6551 Some(names)
6552 } else {
6553 None
6554 };
6555 if matches!(self.peek(), Token::Select) {
6558 let select_stmt = match self.parse_select_stmt()? {
6559 Statement::Select(s) => s,
6560 other => {
6561 return Err(self.err(alloc::format!(
6562 "expected SELECT after INSERT INTO ... target, got {other:?}"
6563 )));
6564 }
6565 };
6566 let on_conflict = self.parse_optional_on_conflict()?;
6567 let returning = self.parse_optional_returning()?;
6568 return Ok(Statement::Insert(InsertStatement {
6569 table,
6570 columns,
6571 rows: Vec::new(),
6572 select_source: Some(Box::new(select_stmt)),
6573 on_conflict,
6574 returning,
6575 }));
6576 }
6577 if !matches!(self.peek(), Token::Values) {
6578 return Err(self.err(format!(
6579 "expected VALUES or SELECT after table name, got {:?}",
6580 self.peek()
6581 )));
6582 }
6583 self.advance();
6584 if !matches!(self.peek(), Token::LParen) {
6585 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
6586 }
6587 let mut rows = Vec::new();
6588 loop {
6589 if !matches!(self.peek(), Token::LParen) {
6591 return Err(self.err(format!(
6592 "expected '(' for next VALUES tuple, got {:?}",
6593 self.peek()
6594 )));
6595 }
6596 self.advance();
6597 let mut tuple = Vec::new();
6598 loop {
6599 tuple.push(self.parse_expr(0)?);
6600 match self.peek() {
6601 Token::Comma => {
6602 self.advance();
6603 }
6604 Token::RParen => {
6605 self.advance();
6606 break;
6607 }
6608 other => {
6609 return Err(self.err(format!(
6610 "expected ',' or ')' in VALUES tuple, got {other:?}"
6611 )));
6612 }
6613 }
6614 }
6615 if tuple.is_empty() {
6616 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
6617 }
6618 rows.push(tuple);
6619 if matches!(self.peek(), Token::Comma) {
6621 self.advance();
6622 } else {
6623 break;
6624 }
6625 }
6626 let on_conflict = self.parse_optional_on_conflict()?;
6627 let returning = self.parse_optional_returning()?;
6628 Ok(Statement::Insert(InsertStatement {
6629 table,
6630 columns,
6631 rows,
6632 select_source: None,
6633 on_conflict,
6634 returning,
6635 }))
6636 }
6637
6638 fn parse_optional_on_conflict(
6643 &mut self,
6644 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
6645 if !matches!(self.peek(), Token::On) {
6646 return Ok(None);
6647 }
6648 let next_is_conflict = matches!(
6651 self.tokens.get(self.pos + 1),
6652 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
6653 );
6654 if !next_is_conflict {
6655 return Ok(None);
6656 }
6657 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
6661 if matches!(self.peek(), Token::LParen) {
6662 self.advance();
6663 loop {
6664 target_columns.push(self.expect_ident_like()?);
6665 match self.peek() {
6666 Token::Comma => {
6667 self.advance();
6668 }
6669 Token::RParen => {
6670 self.advance();
6671 break;
6672 }
6673 other => {
6674 return Err(self.err(alloc::format!(
6675 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
6676 )));
6677 }
6678 }
6679 }
6680 }
6681 match self.advance() {
6683 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
6684 other => {
6685 return Err(self.err(alloc::format!(
6686 "expected DO after ON CONFLICT [(…)], got {other:?}"
6687 )));
6688 }
6689 }
6690 let action = match self.advance() {
6692 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
6693 crate::ast::OnConflictAction::Nothing
6694 }
6695 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
6696 self.parse_on_conflict_update_action()?
6697 }
6698 other => {
6699 return Err(self.err(alloc::format!(
6700 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
6701 )));
6702 }
6703 };
6704 Ok(Some(crate::ast::OnConflictClause {
6705 target_columns,
6706 action,
6707 }))
6708 }
6709
6710 fn parse_on_conflict_update_action(
6714 &mut self,
6715 ) -> Result<crate::ast::OnConflictAction, ParseError> {
6716 match self.advance() {
6718 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
6719 other => {
6720 return Err(self.err(alloc::format!(
6721 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
6722 )));
6723 }
6724 }
6725 let mut assignments: Vec<(String, Expr)> = Vec::new();
6726 loop {
6727 let col = self.expect_ident_like()?;
6728 if !matches!(self.peek(), Token::Eq) {
6729 return Err(self.err(alloc::format!(
6730 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
6731 self.peek()
6732 )));
6733 }
6734 self.advance();
6735 let value = self.parse_expr(0)?;
6736 assignments.push((col, value));
6737 if matches!(self.peek(), Token::Comma) {
6738 self.advance();
6739 continue;
6740 }
6741 break;
6742 }
6743 let where_ = if matches!(self.peek(), Token::Where) {
6744 self.advance();
6745 Some(self.parse_expr(0)?)
6746 } else {
6747 None
6748 };
6749 Ok(crate::ast::OnConflictAction::Update {
6750 assignments,
6751 where_,
6752 })
6753 }
6754
6755 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
6756 let mut items = Vec::new();
6757 loop {
6758 items.push(self.parse_select_item()?);
6759 if matches!(self.peek(), Token::Comma) {
6760 self.advance();
6761 } else {
6762 break;
6763 }
6764 }
6765 Ok(items)
6766 }
6767
6768 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
6769 if matches!(self.peek(), Token::Star) {
6770 self.advance();
6771 return Ok(SelectItem::Wildcard);
6772 }
6773 let expr = self.parse_expr(0)?;
6774 let alias = self.parse_optional_alias();
6775 Ok(SelectItem::Expr { expr, alias })
6776 }
6777
6778 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
6779 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
6785 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6786 {
6787 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
6791 Statement::Select(s) => s,
6792 other => {
6793 return Err(self.err(alloc::format!(
6794 "expected SELECT inside LATERAL ( … ), got {other:?}"
6795 )));
6796 }
6797 };
6798 if !matches!(self.peek(), Token::RParen) {
6799 return Err(self.err(alloc::format!(
6800 "expected ')' after LATERAL subquery, got {:?}",
6801 self.peek()
6802 )));
6803 }
6804 self.advance();
6805 let alias_ident = self.parse_optional_alias();
6806 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
6807 return Ok(TableRef {
6808 name,
6809 alias: alias_ident,
6810 as_of_segment: None,
6811 unnest_expr: None,
6812 unnest_column_aliases: Vec::new(),
6813 generate_series_args: None,
6814 lateral_subquery: Some(Box::new(inner)),
6815 });
6816 }
6817 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
6821 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6822 {
6823 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
6826 if !matches!(self.peek(), Token::RParen) {
6827 return Err(self.err(alloc::format!(
6828 "expected ')' after unnest() argument, got {:?}",
6829 self.peek()
6830 )));
6831 }
6832 self.advance();
6833 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
6834 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
6835 return Ok(TableRef {
6836 name,
6837 alias: alias_ident,
6838 as_of_segment: None,
6839 unnest_expr: Some(Box::new(expr)),
6840 unnest_column_aliases,
6841 generate_series_args: None,
6842 lateral_subquery: None,
6843 });
6844 }
6845 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
6855 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6856 {
6857 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
6860 loop {
6861 args.push(self.parse_expr(0)?);
6862 if matches!(self.peek(), Token::Comma) {
6863 self.advance();
6864 continue;
6865 }
6866 break;
6867 }
6868 if !matches!(self.peek(), Token::RParen) {
6869 return Err(self.err(alloc::format!(
6870 "expected ')' after generate_series() arguments, got {:?}",
6871 self.peek()
6872 )));
6873 }
6874 self.advance();
6875 if args.len() < 2 || args.len() > 3 {
6876 return Err(self.err(alloc::format!(
6877 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
6878 args.len()
6879 )));
6880 }
6881 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
6882 let name = alias_ident
6883 .clone()
6884 .unwrap_or_else(|| "generate_series".to_string());
6885 return Ok(TableRef {
6886 name,
6887 alias: alias_ident,
6888 as_of_segment: None,
6889 unnest_expr: None,
6890 unnest_column_aliases: Vec::new(),
6891 generate_series_args: Some(args),
6892 lateral_subquery: None,
6893 });
6894 }
6895 let name = if let Some(synth) = self.try_peek_meta_qualified() {
6904 synth
6905 } else {
6906 self.expect_ident_like()?
6907 };
6908 let as_of_segment = if matches!(self.peek(), Token::As)
6914 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
6915 {
6916 self.advance(); self.advance(); let kw = match self.peek().clone() {
6919 Token::Ident(s) | Token::QuotedIdent(s) => s,
6920 other => {
6921 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
6922 }
6923 };
6924 if !kw.eq_ignore_ascii_case("segment") {
6925 return Err(self.err(format!(
6926 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
6927 )));
6928 }
6929 self.advance();
6930 let id = match self.advance() {
6933 Token::String(s) => s
6934 .parse::<u32>()
6935 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
6936 Token::Integer(n) => u32::try_from(n)
6937 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
6938 other => {
6939 return Err(self.err(format!(
6940 "expected segment id literal after AS OF SEGMENT, got {other:?}"
6941 )));
6942 }
6943 };
6944 Some(id)
6945 } else {
6946 None
6947 };
6948 let alias = self.parse_optional_alias();
6949 Ok(TableRef {
6950 name,
6951 alias,
6952 as_of_segment,
6953 unnest_expr: None,
6954 unnest_column_aliases: Vec::new(),
6955 generate_series_args: None,
6956 lateral_subquery: None,
6957 })
6958 }
6959
6960 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
6966 let alias = self.parse_optional_alias();
6967 if alias.is_none() {
6968 return (None, Vec::new());
6969 }
6970 let mut cols: Vec<String> = Vec::new();
6971 if matches!(self.peek(), Token::LParen) {
6972 self.advance();
6973 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
6974 self.advance();
6975 cols.push(s);
6976 if matches!(self.peek(), Token::Comma) {
6977 self.advance();
6978 continue;
6979 }
6980 break;
6981 }
6982 if matches!(self.peek(), Token::RParen) {
6983 self.advance();
6984 }
6985 }
6986 (alias, cols)
6987 }
6988
6989 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
6994 let primary = self.parse_table_ref()?;
6995 let mut joins = Vec::new();
6996 loop {
6997 if matches!(self.peek(), Token::Comma) {
6999 self.advance();
7000 let table = self.parse_table_ref()?;
7001 joins.push(FromJoin {
7002 kind: JoinKind::Cross,
7003 table,
7004 on: None,
7005 });
7006 continue;
7007 }
7008 let kind =
7011 match self.peek() {
7012 Token::Inner => {
7013 self.advance();
7014 if !matches!(self.peek(), Token::Join) {
7015 return Err(self
7016 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7017 }
7018 self.advance();
7019 JoinKind::Inner
7020 }
7021 Token::Left => {
7022 self.advance();
7023 if matches!(self.peek(), Token::Outer) {
7024 self.advance();
7025 }
7026 if !matches!(self.peek(), Token::Join) {
7027 return Err(self.err(format!(
7028 "expected JOIN after LEFT [OUTER], got {:?}",
7029 self.peek()
7030 )));
7031 }
7032 self.advance();
7033 JoinKind::Left
7034 }
7035 Token::Cross => {
7036 self.advance();
7037 if !matches!(self.peek(), Token::Join) {
7038 return Err(self
7039 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7040 }
7041 self.advance();
7042 JoinKind::Cross
7043 }
7044 Token::Join => {
7045 self.advance();
7046 JoinKind::Inner
7047 }
7048 _ => break,
7049 };
7050 let table = self.parse_table_ref()?;
7051 let on = if matches!(self.peek(), Token::On) {
7052 self.advance();
7053 Some(self.parse_expr(0)?)
7054 } else if kind == JoinKind::Cross {
7055 None
7056 } else {
7057 return Err(self.err(format!(
7058 "expected ON after {:?} JOIN, got {:?}",
7059 kind,
7060 self.peek()
7061 )));
7062 };
7063 joins.push(FromJoin { kind, table, on });
7064 }
7065 Ok(FromClause { primary, joins })
7066 }
7067
7068 fn parse_optional_alias(&mut self) -> Option<String> {
7073 if matches!(self.peek(), Token::As) {
7074 self.advance();
7075 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7080 return self.expect_ident_like().ok();
7081 }
7082 return None;
7083 }
7084 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7092 if is_alias_stopword(s) {
7093 return None;
7094 }
7095 return self.expect_ident_like().ok();
7096 }
7097 None
7098 }
7099
7100 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7102 let mut lhs = self.parse_unary()?;
7103 while let Some((op, prec)) = binop_from(self.peek()) {
7104 if prec < min_prec {
7105 break;
7106 }
7107 self.advance();
7108 let any_kind = match self.peek() {
7113 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7114 Some(false)
7115 }
7116 Token::Ident(s) | Token::QuotedIdent(s)
7117 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7118 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7119 {
7120 Some(s.eq_ignore_ascii_case("any"))
7121 }
7122 _ => None,
7123 };
7124 if let Some(is_any) = any_kind {
7125 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7128 if !matches!(self.peek(), Token::RParen) {
7129 return Err(self.err(alloc::format!(
7130 "expected ')' after ANY/ALL argument, got {:?}",
7131 self.peek()
7132 )));
7133 }
7134 self.advance();
7135 lhs = Expr::AnyAll {
7136 expr: Box::new(lhs),
7137 op,
7138 array: Box::new(arr),
7139 is_any,
7140 };
7141 continue;
7142 }
7143 let rhs = self.parse_expr(prec + 1)?;
7144 lhs = Expr::Binary {
7145 lhs: Box::new(lhs),
7146 op,
7147 rhs: Box::new(rhs),
7148 };
7149 }
7150 Ok(lhs)
7151 }
7152
7153 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7154 match self.peek() {
7155 Token::Not => {
7156 self.advance();
7157 let e = self.parse_expr(3)?;
7160 Ok(Expr::Unary {
7161 op: UnOp::Not,
7162 expr: Box::new(e),
7163 })
7164 }
7165 Token::Minus => {
7166 self.advance();
7167 let e = self.parse_expr(8)?;
7170 Ok(Expr::Unary {
7171 op: UnOp::Neg,
7172 expr: Box::new(e),
7173 })
7174 }
7175 _ => self.parse_atom(),
7176 }
7177 }
7178
7179 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7180 let tok_pos = self.pos;
7181 match self.advance() {
7182 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7183 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7184 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7185 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7186 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7187 Token::Null => Ok(Expr::Literal(Literal::Null)),
7188 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7192 Token::LParen => {
7193 if matches!(self.peek(), Token::Select) {
7197 let inner = self.parse_select_stmt()?;
7198 match self.advance() {
7199 Token::RParen => {
7200 let Statement::Select(s) = inner else {
7201 unreachable!("parse_select_stmt returns Select")
7202 };
7203 Ok(Expr::ScalarSubquery(Box::new(s)))
7204 }
7205 other => Err(ParseError {
7206 message: format!("expected ')' after scalar subquery, got {other:?}"),
7207 token_pos: self.pos.saturating_sub(1),
7208 }),
7209 }
7210 } else {
7211 let e = self.parse_expr(0)?;
7212 match self.advance() {
7213 Token::RParen => Ok(e),
7214 other => Err(ParseError {
7215 message: format!("expected ')', got {other:?}"),
7216 token_pos: self.pos.saturating_sub(1),
7217 }),
7218 }
7219 }
7220 }
7221 Token::LBracket => self.parse_vector_literal_body(),
7222 Token::Extract => self.parse_extract_atom(),
7223 Token::Interval => self.parse_interval_atom(),
7224 Token::Left if matches!(self.peek(), Token::LParen) => {
7231 self.advance(); let mut args = Vec::new();
7233 if !matches!(self.peek(), Token::RParen) {
7234 loop {
7235 args.push(self.parse_expr(0)?);
7236 match self.peek() {
7237 Token::Comma => {
7238 self.advance();
7239 }
7240 Token::RParen => break,
7241 other => {
7242 return Err(self.err(alloc::format!(
7243 "expected ',' or ')' in left() args, got {other:?}"
7244 )));
7245 }
7246 }
7247 }
7248 }
7249 self.advance(); Ok(Expr::FunctionCall {
7251 name: "left".into(),
7252 args,
7253 })
7254 }
7255 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7260 self.parse_exists_atom(false)
7261 }
7262 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7266 self.parse_case_atom()
7267 }
7268 Token::Ident(s) | Token::QuotedIdent(s)
7272 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7273 {
7274 self.advance(); let mut items: Vec<Expr> = Vec::new();
7276 if !matches!(self.peek(), Token::RBracket) {
7277 loop {
7278 items.push(self.parse_expr(0)?);
7279 match self.peek() {
7280 Token::Comma => {
7281 self.advance();
7282 }
7283 Token::RBracket => break,
7284 other => {
7285 return Err(self.err(alloc::format!(
7286 "expected ',' or ']' in ARRAY literal, got {other:?}"
7287 )));
7288 }
7289 }
7290 }
7291 }
7292 self.advance(); Ok(Expr::Array(items))
7294 }
7295 Token::Ident(s) | Token::QuotedIdent(s)
7310 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7311 {
7312 self.parse_match_against_atom()
7313 }
7314 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7315 other => Err(ParseError {
7316 message: format!("unexpected token {other:?} in expression"),
7317 token_pos: tok_pos,
7318 }),
7319 }
7320 .and_then(|atom| self.finish_postfix_casts(atom))
7322 }
7323
7324 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7327 loop {
7328 if matches!(self.peek(), Token::DoubleColon) {
7329 self.advance();
7330 let target = match self.advance() {
7335 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7336 "int" | "integer" | "int4" => {
7337 if matches!(self.peek(), Token::LBracket)
7338 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7339 {
7340 self.advance();
7341 self.advance();
7342 CastTarget::IntArray
7343 } else {
7344 CastTarget::Int
7345 }
7346 }
7347 "bigint" | "int8" => {
7348 if matches!(self.peek(), Token::LBracket)
7349 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7350 {
7351 self.advance();
7352 self.advance();
7353 CastTarget::BigIntArray
7354 } else {
7355 CastTarget::BigInt
7356 }
7357 }
7358 "float" | "double" | "real" => CastTarget::Float,
7359 "text" => {
7360 if matches!(self.peek(), Token::LBracket)
7362 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7363 {
7364 self.advance();
7365 self.advance();
7366 CastTarget::TextArray
7367 } else {
7368 CastTarget::Text
7369 }
7370 }
7371 "bool" | "boolean" => CastTarget::Bool,
7372 "vector" => CastTarget::Vector,
7373 "date" => CastTarget::Date,
7374 "timestamp" | "datetime" => CastTarget::Timestamp,
7375 "timestamptz" => CastTarget::Timestamptz,
7376 "interval" => CastTarget::Interval,
7377 "json" => CastTarget::Json,
7378 "jsonb" => CastTarget::Jsonb,
7379 "regtype" => CastTarget::RegType,
7380 "regclass" => CastTarget::RegClass,
7381 "tsvector" => CastTarget::TsVector,
7385 "tsquery" => CastTarget::TsQuery,
7386 "uuid" => CastTarget::Uuid,
7389 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7394 other => {
7395 return Err(ParseError {
7396 message: format!("unsupported cast target `::{other}`"),
7397 token_pos: self.pos.saturating_sub(1),
7398 });
7399 }
7400 },
7401 Token::Interval => CastTarget::Interval,
7402 other => {
7403 return Err(ParseError {
7404 message: format!("expected type ident after `::`, got {other:?}"),
7405 token_pos: self.pos.saturating_sub(1),
7406 });
7407 }
7408 };
7409 expr = Expr::Cast {
7410 expr: Box::new(expr),
7411 target,
7412 };
7413 continue;
7414 }
7415 if matches!(self.peek(), Token::Is) {
7416 self.advance();
7417 let negated = if matches!(self.peek(), Token::Not) {
7418 self.advance();
7419 true
7420 } else {
7421 false
7422 };
7423 if matches!(self.peek(), Token::Distinct) {
7426 self.advance();
7427 if !matches!(self.peek(), Token::From) {
7428 return Err(self.err(format!(
7429 "expected FROM after IS{} DISTINCT, got {:?}",
7430 if negated { " NOT" } else { "" },
7431 self.peek()
7432 )));
7433 }
7434 self.advance();
7435 let rhs = self.parse_expr(20)?;
7439 let op = if negated {
7440 BinOp::IsNotDistinctFrom
7441 } else {
7442 BinOp::IsDistinctFrom
7443 };
7444 expr = Expr::Binary {
7445 op,
7446 lhs: Box::new(expr),
7447 rhs: Box::new(rhs),
7448 };
7449 continue;
7450 }
7451 if !matches!(self.peek(), Token::Null) {
7452 return Err(self.err(format!(
7453 "expected NULL or DISTINCT after IS{}, got {:?}",
7454 if negated { " NOT" } else { "" },
7455 self.peek()
7456 )));
7457 }
7458 self.advance();
7459 expr = Expr::IsNull {
7460 expr: Box::new(expr),
7461 negated,
7462 };
7463 continue;
7464 }
7465 let negated = if matches!(self.peek(), Token::Not) {
7469 let next = self.tokens.get(self.pos + 1);
7470 matches!(next, Some(Token::Between | Token::In | Token::Like))
7471 } else {
7472 false
7473 };
7474 if negated {
7475 self.advance();
7476 }
7477 if matches!(self.peek(), Token::Between) {
7478 expr = self.parse_between_tail(expr, negated)?;
7479 continue;
7480 }
7481 if matches!(self.peek(), Token::In) {
7482 expr = self.parse_in_tail(expr, negated)?;
7483 continue;
7484 }
7485 if matches!(self.peek(), Token::Like) {
7486 self.advance();
7487 let pattern = self.parse_expr(5)?;
7490 expr = Expr::Like {
7491 expr: Box::new(expr),
7492 pattern: Box::new(pattern),
7493 negated,
7494 };
7495 continue;
7496 }
7497 if matches!(self.peek(), Token::LBracket) {
7501 self.advance();
7502 let index = self.parse_expr(0)?;
7503 if !matches!(self.peek(), Token::RBracket) {
7504 return Err(self.err(alloc::format!(
7505 "expected ']' after array index, got {:?}",
7506 self.peek()
7507 )));
7508 }
7509 self.advance();
7510 expr = Expr::ArraySubscript {
7511 target: Box::new(expr),
7512 index: Box::new(index),
7513 };
7514 continue;
7515 }
7516 return Ok(expr);
7517 }
7518 }
7519
7520 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7524 self.advance(); let low = self.parse_expr(5)?;
7526 if !matches!(self.peek(), Token::And) {
7527 return Err(self.err(format!(
7528 "expected AND after BETWEEN low bound, got {:?}",
7529 self.peek()
7530 )));
7531 }
7532 self.advance();
7533 let high = self.parse_expr(5)?;
7534 let target = Box::new(expr);
7535 let combined = Expr::Binary {
7536 lhs: Box::new(Expr::Binary {
7537 lhs: target.clone(),
7538 op: BinOp::GtEq,
7539 rhs: Box::new(low),
7540 }),
7541 op: BinOp::And,
7542 rhs: Box::new(Expr::Binary {
7543 lhs: target,
7544 op: BinOp::LtEq,
7545 rhs: Box::new(high),
7546 }),
7547 };
7548 Ok(maybe_not(combined, negated))
7549 }
7550
7551 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
7556 let mut recursive = false;
7561 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
7562 && s.eq_ignore_ascii_case("recursive")
7563 {
7564 self.advance();
7565 recursive = true;
7566 }
7567 let mut ctes = Vec::new();
7568 loop {
7569 let name = self.expect_ident_like()?;
7570 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
7574 self.advance();
7575 let mut names = Vec::new();
7576 loop {
7577 names.push(self.expect_ident_like()?);
7578 if matches!(self.peek(), Token::Comma) {
7579 self.advance();
7580 continue;
7581 }
7582 break;
7583 }
7584 if !matches!(self.peek(), Token::RParen) {
7585 return Err(self.err(format!(
7586 "expected ')' to close CTE column list, got {:?}",
7587 self.peek()
7588 )));
7589 }
7590 self.advance();
7591 names
7592 } else {
7593 Vec::new()
7594 };
7595 if !matches!(self.peek(), Token::As) {
7599 return Err(self.err(format!(
7600 "expected AS after CTE name {name:?}, got {:?}",
7601 self.peek()
7602 )));
7603 }
7604 self.advance();
7605 if !matches!(self.peek(), Token::LParen) {
7606 return Err(self.err(format!(
7607 "expected '(' after AS in WITH clause, got {:?}",
7608 self.peek()
7609 )));
7610 }
7611 self.advance();
7612 if !matches!(self.peek(), Token::Select) {
7613 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
7614 }
7615 let inner = self.parse_select_stmt()?;
7616 if !matches!(self.peek(), Token::RParen) {
7617 return Err(self.err(format!(
7618 "expected ')' after CTE body, got {:?}",
7619 self.peek()
7620 )));
7621 }
7622 self.advance();
7623 let Statement::Select(body) = inner else {
7624 unreachable!("parse_select_stmt returns Select")
7625 };
7626 ctes.push(crate::ast::Cte {
7627 name,
7628 body,
7629 recursive,
7630 column_overrides,
7631 });
7632 if matches!(self.peek(), Token::Comma) {
7633 self.advance();
7634 continue;
7635 }
7636 break;
7637 }
7638 if !matches!(self.peek(), Token::Select) {
7640 return Err(self.err(format!(
7641 "expected SELECT after WITH clause, got {:?}",
7642 self.peek()
7643 )));
7644 }
7645 let body_stmt = self.parse_select_stmt()?;
7646 let Statement::Select(mut body) = body_stmt else {
7647 unreachable!()
7648 };
7649 body.ctes = ctes;
7650 Ok(Statement::Select(body))
7651 }
7652
7653 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
7662 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
7666 None
7667 } else {
7668 Some(Box::new(self.parse_expr(0)?))
7669 };
7670 let mut branches: Vec<(Expr, Expr)> = Vec::new();
7671 loop {
7672 match self.peek() {
7673 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
7674 self.advance();
7675 let cond = self.parse_expr(0)?;
7676 match self.peek() {
7677 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
7678 self.advance();
7679 }
7680 other => {
7681 return Err(self.err(alloc::format!(
7682 "expected THEN after CASE WHEN <expr>, got {other:?}"
7683 )));
7684 }
7685 }
7686 let value = self.parse_expr(0)?;
7687 branches.push((cond, value));
7688 }
7689 _ => break,
7690 }
7691 }
7692 if branches.is_empty() {
7693 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
7694 }
7695 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
7696 {
7697 self.advance();
7698 Some(Box::new(self.parse_expr(0)?))
7699 } else {
7700 None
7701 };
7702 match self.peek() {
7703 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
7704 self.advance();
7705 }
7706 other => {
7707 return Err(self.err(alloc::format!(
7708 "expected END to close CASE expression, got {other:?}"
7709 )));
7710 }
7711 }
7712 Ok(Expr::Case {
7713 operand,
7714 branches,
7715 else_branch,
7716 })
7717 }
7718
7719 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
7720 if !matches!(self.peek(), Token::LParen) {
7721 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
7722 }
7723 self.advance();
7724 let inner = self.parse_select_stmt()?;
7725 if !matches!(self.peek(), Token::RParen) {
7726 return Err(self.err(format!(
7727 "expected ')' after EXISTS-subquery, got {:?}",
7728 self.peek()
7729 )));
7730 }
7731 self.advance();
7732 let Statement::Select(s) = inner else {
7733 unreachable!("parse_select_stmt returns Select")
7734 };
7735 Ok(Expr::Exists {
7736 subquery: Box::new(s),
7737 negated,
7738 })
7739 }
7740
7741 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7742 self.advance(); if !matches!(self.peek(), Token::LParen) {
7744 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
7745 }
7746 self.advance();
7747 if matches!(self.peek(), Token::Select) {
7749 let inner = self.parse_select_stmt()?;
7750 if !matches!(self.peek(), Token::RParen) {
7751 return Err(self.err(format!(
7752 "expected ')' after IN-subquery, got {:?}",
7753 self.peek()
7754 )));
7755 }
7756 self.advance();
7757 let Statement::Select(s) = inner else {
7758 unreachable!("parse_select_stmt always returns Statement::Select")
7759 };
7760 return Ok(Expr::InSubquery {
7761 expr: Box::new(expr),
7762 subquery: Box::new(s),
7763 negated,
7764 });
7765 }
7766 let mut elements = Vec::new();
7767 if !matches!(self.peek(), Token::RParen) {
7768 loop {
7769 elements.push(self.parse_expr(0)?);
7770 match self.peek() {
7771 Token::Comma => {
7772 self.advance();
7773 }
7774 Token::RParen => break,
7775 other => {
7776 return Err(
7777 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
7778 );
7779 }
7780 }
7781 }
7782 }
7783 self.advance(); let target = Box::new(expr);
7785 let combined = if elements.is_empty() {
7786 Expr::Literal(Literal::Bool(false))
7787 } else {
7788 let mut iter = elements.into_iter();
7789 let first = iter.next().unwrap();
7790 let mut acc = Expr::Binary {
7791 lhs: target.clone(),
7792 op: BinOp::Eq,
7793 rhs: Box::new(first),
7794 };
7795 for elt in iter {
7796 acc = Expr::Binary {
7797 lhs: Box::new(acc),
7798 op: BinOp::Or,
7799 rhs: Box::new(Expr::Binary {
7800 lhs: target.clone(),
7801 op: BinOp::Eq,
7802 rhs: Box::new(elt),
7803 }),
7804 };
7805 }
7806 acc
7807 };
7808 Ok(maybe_not(combined, negated))
7809 }
7810
7811 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
7832 if !matches!(self.peek(), Token::LParen) {
7835 return Err(self.err(alloc::format!(
7836 "expected '(' after MATCH, got {:?}",
7837 self.peek()
7838 )));
7839 }
7840 self.advance();
7841 let mut cols: Vec<Expr> = Vec::new();
7842 loop {
7843 cols.push(self.parse_expr(0)?);
7844 match self.peek() {
7845 Token::Comma => {
7846 self.advance();
7847 }
7848 Token::RParen => break,
7849 other => {
7850 return Err(self.err(alloc::format!(
7851 "expected ',' or ')' in MATCH column list, got {other:?}"
7852 )));
7853 }
7854 }
7855 }
7856 self.advance(); match self.peek() {
7859 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
7860 self.advance();
7861 }
7862 other => {
7863 return Err(self.err(alloc::format!(
7864 "expected AGAINST after MATCH column list, got {other:?}"
7865 )));
7866 }
7867 }
7868 if !matches!(self.peek(), Token::LParen) {
7869 return Err(self.err(alloc::format!(
7870 "expected '(' after AGAINST, got {:?}",
7871 self.peek()
7872 )));
7873 }
7874 self.advance();
7875 let term = match self.advance() {
7885 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
7886 Token::Placeholder(n) => Expr::Placeholder(n),
7887 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
7888 qualifier: None,
7889 name: s,
7890 }),
7891 other => {
7892 return Err(self.err(alloc::format!(
7893 "MATCH ... AGAINST(<term>) expects a string literal, \
7894 bound parameter, or column ref, got {other:?}"
7895 )));
7896 }
7897 };
7898 loop {
7903 match self.peek() {
7904 Token::In => {
7907 self.advance();
7908 }
7909 Token::Ident(s) | Token::QuotedIdent(s)
7910 if s.eq_ignore_ascii_case("natural")
7911 || s.eq_ignore_ascii_case("language")
7912 || s.eq_ignore_ascii_case("boolean")
7913 || s.eq_ignore_ascii_case("mode")
7914 || s.eq_ignore_ascii_case("with")
7915 || s.eq_ignore_ascii_case("query")
7916 || s.eq_ignore_ascii_case("expansion") =>
7917 {
7918 self.advance();
7919 }
7920 _ => break,
7921 }
7922 }
7923 if !matches!(self.peek(), Token::RParen) {
7924 return Err(self.err(alloc::format!(
7925 "expected ')' to close AGAINST, got {:?}",
7926 self.peek()
7927 )));
7928 }
7929 self.advance();
7930 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
7933 let plainto = Expr::FunctionCall {
7934 name: String::from("plainto_tsquery"),
7935 args: alloc::vec![simple_lit(), term.clone()],
7936 };
7937 let mut folded: Option<Expr> = None;
7938 for col in cols {
7939 let to_tsv = Expr::FunctionCall {
7940 name: String::from("to_tsvector"),
7941 args: alloc::vec![simple_lit(), col],
7942 };
7943 let leaf = Expr::Binary {
7944 lhs: Box::new(to_tsv),
7945 op: crate::ast::BinOp::TsMatch,
7946 rhs: Box::new(plainto.clone()),
7947 };
7948 folded = Some(match folded {
7949 None => leaf,
7950 Some(prev) => Expr::Binary {
7951 lhs: Box::new(prev),
7952 op: crate::ast::BinOp::Or,
7953 rhs: Box::new(leaf),
7954 },
7955 });
7956 }
7957 match folded {
7958 Some(e) => Ok(e),
7959 None => Err(self.err(String::from(
7960 "MATCH(...) AGAINST(...) requires at least one column",
7961 ))),
7962 }
7963 }
7964
7965 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
7966 if !matches!(self.peek(), Token::LParen) {
7967 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
7968 }
7969 self.advance();
7970 let field_name = self.expect_ident_like()?;
7971 let field = match field_name.to_ascii_lowercase().as_str() {
7972 "year" => ExtractField::Year,
7973 "month" => ExtractField::Month,
7974 "day" => ExtractField::Day,
7975 "hour" => ExtractField::Hour,
7976 "minute" => ExtractField::Minute,
7977 "second" => ExtractField::Second,
7978 "microsecond" | "microseconds" => ExtractField::Microsecond,
7979 other => {
7980 return Err(self.err(format!(
7981 "unknown EXTRACT field {other:?}; \
7982 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
7983 )));
7984 }
7985 };
7986 if !matches!(self.peek(), Token::From) {
7987 return Err(self.err(format!(
7988 "expected FROM after EXTRACT field, got {:?}",
7989 self.peek()
7990 )));
7991 }
7992 self.advance();
7993 let source = self.parse_expr(0)?;
7994 if !matches!(self.peek(), Token::RParen) {
7995 return Err(self.err(format!(
7996 "expected ')' to close EXTRACT, got {:?}",
7997 self.peek()
7998 )));
7999 }
8000 self.advance();
8001 Ok(Expr::Extract {
8002 field,
8003 source: Box::new(source),
8004 })
8005 }
8006
8007 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8012 let tok = self.advance();
8013 let Token::String(text) = tok else {
8014 return Err(self.err(format!(
8015 "expected string literal after INTERVAL, got {tok:?}"
8016 )));
8017 };
8018 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8019 message: format!(
8020 "cannot parse INTERVAL {text:?}; \
8021 expected `<n> <unit> [<n> <unit> ...]` with units \
8022 microsecond[s], millisecond[s], second[s], minute[s], \
8023 hour[s], day[s], week[s], month[s], year[s]"
8024 ),
8025 token_pos: self.pos.saturating_sub(1),
8026 })?;
8027 Ok(Expr::Literal(Literal::Interval {
8028 months,
8029 micros,
8030 text,
8031 }))
8032 }
8033
8034 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8035 let mut elems = Vec::new();
8036 if matches!(self.peek(), Token::RBracket) {
8037 self.advance();
8038 return Ok(Expr::Literal(Literal::Vector(elems)));
8039 }
8040 loop {
8041 let e = self.parse_expr(0)?;
8042 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8043 message: format!("vector element must be a numeric literal, got {e:?}"),
8044 token_pos: self.pos,
8045 })?;
8046 elems.push(x);
8047 match self.peek() {
8048 Token::Comma => {
8049 self.advance();
8050 }
8051 Token::RBracket => {
8052 self.advance();
8053 break;
8054 }
8055 other => {
8056 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8057 }
8058 }
8059 }
8060 Ok(Expr::Literal(Literal::Vector(elems)))
8061 }
8062
8063 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8072 let Token::Ident(s) = self.peek().clone() else {
8073 return NullTreatment::Respect;
8074 };
8075 let is_ignore = s.eq_ignore_ascii_case("ignore");
8076 let is_respect = s.eq_ignore_ascii_case("respect");
8077 if !is_ignore && !is_respect {
8078 return NullTreatment::Respect;
8079 }
8080 if self.pos + 1 < self.tokens.len()
8083 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8084 && s2.eq_ignore_ascii_case("nulls")
8085 {
8086 self.advance();
8087 self.advance();
8088 return if is_ignore {
8089 NullTreatment::Ignore
8090 } else {
8091 NullTreatment::Respect
8092 };
8093 }
8094 NullTreatment::Respect
8095 }
8096
8097 #[allow(clippy::type_complexity)] fn parse_over_clause(
8100 &mut self,
8101 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
8102 if !matches!(self.peek(), Token::LParen) {
8103 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8104 }
8105 self.advance();
8106 let mut partition_by = Vec::new();
8107 let mut order_by = Vec::new();
8108 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8110 && s.eq_ignore_ascii_case("partition")
8111 {
8112 self.advance();
8113 if !matches!(self.peek(), Token::By) {
8114 return Err(self.err(format!(
8115 "expected BY after PARTITION, got {:?}",
8116 self.peek()
8117 )));
8118 }
8119 self.advance();
8120 loop {
8121 partition_by.push(self.parse_expr(0)?);
8122 if matches!(self.peek(), Token::Comma) {
8123 self.advance();
8124 continue;
8125 }
8126 break;
8127 }
8128 }
8129 if matches!(self.peek(), Token::Order) {
8131 self.advance();
8132 if !matches!(self.peek(), Token::By) {
8133 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8134 }
8135 self.advance();
8136 loop {
8137 let e = self.parse_expr(0)?;
8138 let desc = if matches!(self.peek(), Token::Desc) {
8139 self.advance();
8140 true
8141 } else if matches!(self.peek(), Token::Asc) {
8142 self.advance();
8143 false
8144 } else {
8145 false
8146 };
8147 order_by.push((e, desc));
8148 if matches!(self.peek(), Token::Comma) {
8149 self.advance();
8150 continue;
8151 }
8152 break;
8153 }
8154 }
8155 let mut frame: Option<WindowFrame> = None;
8159 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8160 let kind = if s.eq_ignore_ascii_case("rows") {
8161 Some(FrameKind::Rows)
8162 } else if s.eq_ignore_ascii_case("range") {
8163 Some(FrameKind::Range)
8164 } else {
8165 None
8166 };
8167 if let Some(kind) = kind {
8168 self.advance();
8169 frame = Some(self.parse_frame_tail(kind)?);
8170 }
8171 }
8172 if !matches!(self.peek(), Token::RParen) {
8173 return Err(self.err(format!(
8174 "expected ')' to close OVER clause, got {:?}",
8175 self.peek()
8176 )));
8177 }
8178 self.advance();
8179 Ok((partition_by, order_by, frame))
8180 }
8181
8182 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8188 if matches!(self.peek(), Token::Between) {
8189 self.advance();
8190 let start = self.parse_frame_bound()?;
8191 if !matches!(self.peek(), Token::And) {
8192 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8193 }
8194 self.advance();
8195 let end = self.parse_frame_bound()?;
8196 Ok(WindowFrame {
8197 kind,
8198 start,
8199 end: Some(end),
8200 })
8201 } else {
8202 let start = self.parse_frame_bound()?;
8203 Ok(WindowFrame {
8204 kind,
8205 start,
8206 end: None,
8207 })
8208 }
8209 }
8210
8211 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8214 if let Token::Integer(n) = *self.peek() {
8216 self.advance();
8217 let n: u64 = u64::try_from(n).map_err(|_| {
8218 self.err(format!(
8219 "invalid frame offset {n} — expected non-negative integer"
8220 ))
8221 })?;
8222 let dir = self.expect_ident_like()?;
8223 return if dir.eq_ignore_ascii_case("preceding") {
8224 Ok(FrameBound::OffsetPreceding(n))
8225 } else if dir.eq_ignore_ascii_case("following") {
8226 Ok(FrameBound::OffsetFollowing(n))
8227 } else {
8228 Err(self.err(format!(
8229 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8230 )))
8231 };
8232 }
8233 let first = self.expect_ident_like()?;
8234 if first.eq_ignore_ascii_case("unbounded") {
8235 let dir = self.expect_ident_like()?;
8236 return if dir.eq_ignore_ascii_case("preceding") {
8237 Ok(FrameBound::UnboundedPreceding)
8238 } else if dir.eq_ignore_ascii_case("following") {
8239 Ok(FrameBound::UnboundedFollowing)
8240 } else {
8241 Err(self.err(format!(
8242 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8243 )))
8244 };
8245 }
8246 if first.eq_ignore_ascii_case("current") {
8247 let row = self.expect_ident_like()?;
8248 if !row.eq_ignore_ascii_case("row") {
8249 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8250 }
8251 return Ok(FrameBound::CurrentRow);
8252 }
8253 Err(self.err(format!(
8254 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8255 )))
8256 }
8257
8258 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8259 if matches!(self.peek(), Token::Dot) {
8260 self.advance();
8261 let name = self.expect_ident_like()?;
8262 if matches!(self.peek(), Token::LParen) {
8268 return self.finish_ident_atom(name);
8269 }
8270 return Ok(Expr::Column(ColumnName {
8271 qualifier: Some(first),
8272 name,
8273 }));
8274 }
8275 if matches!(self.peek(), Token::LParen) {
8276 self.advance();
8277 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8281 self.advance();
8282 if !matches!(self.peek(), Token::RParen) {
8283 return Err(self.err(format!(
8284 "expected ')' after COUNT(*), got {:?}",
8285 self.peek()
8286 )));
8287 }
8288 self.advance();
8289 let null_treatment = self.parse_null_treatment_modifier();
8291 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8292 && s.eq_ignore_ascii_case("over")
8293 {
8294 self.advance();
8295 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8296 return Ok(Expr::WindowFunction {
8297 name: "count_star".into(),
8298 args: Vec::new(),
8299 partition_by,
8300 order_by,
8301 frame,
8302 null_treatment,
8303 });
8304 }
8305 return Ok(Expr::FunctionCall {
8306 name: "count_star".into(),
8307 args: Vec::new(),
8308 });
8309 }
8310 let mut args = Vec::new();
8312 if !matches!(self.peek(), Token::RParen) {
8313 loop {
8314 args.push(self.parse_expr(0)?);
8315 match self.peek() {
8316 Token::Comma => {
8317 self.advance();
8318 }
8319 Token::RParen => break,
8320 other => {
8321 return Err(self.err(format!(
8322 "expected ',' or ')' in function args, got {other:?}"
8323 )));
8324 }
8325 }
8326 }
8327 }
8328 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8336 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8337 && s.eq_ignore_ascii_case("over")
8338 {
8339 self.advance();
8340 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8341 return Ok(Expr::WindowFunction {
8342 name: first,
8343 args,
8344 partition_by,
8345 order_by,
8346 frame,
8347 null_treatment,
8348 });
8349 }
8350 return Ok(Expr::FunctionCall { name: first, args });
8351 }
8352 let lc = first.to_ascii_lowercase();
8358 if matches!(
8359 lc.as_str(),
8360 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8361 ) {
8362 return Ok(Expr::FunctionCall {
8363 name: lc,
8364 args: Vec::new(),
8365 });
8366 }
8367 Ok(Expr::Column(ColumnName {
8368 qualifier: None,
8369 name: first,
8370 }))
8371 }
8372}
8373
8374fn extract_first_column(expr: &Expr) -> Option<String> {
8382 match expr {
8383 Expr::Column(cn) => Some(cn.name.clone()),
8384 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8385 Expr::Binary { lhs, rhs, .. } => {
8386 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8387 }
8388 Expr::Unary { expr: e, .. } => extract_first_column(e),
8389 _ => None,
8390 }
8391}
8392
8393fn maybe_not(expr: Expr, negated: bool) -> Expr {
8394 if negated {
8395 Expr::Unary {
8396 op: UnOp::Not,
8397 expr: Box::new(expr),
8398 }
8399 } else {
8400 expr
8401 }
8402}
8403
8404fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8405 let pair = match tok {
8406 Token::Or => (BinOp::Or, 1),
8407 Token::And => (BinOp::And, 2),
8408 Token::Eq => (BinOp::Eq, 4),
8409 Token::NotEq => (BinOp::NotEq, 4),
8410 Token::Lt => (BinOp::Lt, 4),
8411 Token::LtEq => (BinOp::LtEq, 4),
8412 Token::Gt => (BinOp::Gt, 4),
8413 Token::GtEq => (BinOp::GtEq, 4),
8414 Token::L2Distance => (BinOp::L2Distance, 5),
8417 Token::InnerProduct => (BinOp::InnerProduct, 5),
8418 Token::CosineDistance => (BinOp::CosineDistance, 5),
8419 Token::Plus => (BinOp::Add, 6),
8420 Token::Minus => (BinOp::Sub, 6),
8421 Token::Concat => (BinOp::Concat, 6),
8424 Token::Star => (BinOp::Mul, 7),
8425 Token::Slash => (BinOp::Div, 7),
8426 Token::JsonGet => (BinOp::JsonGet, 7),
8430 Token::JsonGetText => (BinOp::JsonGetText, 7),
8431 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
8432 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
8433 Token::JsonContains => (BinOp::JsonContains, 7),
8434 Token::TsMatch => (BinOp::TsMatch, 4),
8438 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
8442 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
8443 Token::InetContains => (BinOp::InetContains, 4),
8444 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
8445 Token::InetOverlap => (BinOp::InetOverlap, 4),
8446 _ => return None,
8447 };
8448 Some(pair)
8449}
8450
8451#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
8452fn is_alias_stopword(s: &str) -> bool {
8464 matches!(
8465 s.to_ascii_lowercase().as_str(),
8466 "with"
8467 | "on"
8468 | "where"
8469 | "having"
8470 | "group"
8471 | "order"
8472 | "limit"
8473 | "offset"
8474 | "union"
8475 | "except"
8476 | "intersect"
8477 | "returning"
8478 | "set"
8479 | "values"
8480 | "for"
8481 | "lateral"
8482 | "left"
8483 | "right"
8484 | "inner"
8485 | "outer"
8486 | "full"
8487 | "cross"
8488 | "join"
8489 | "natural"
8490 | "using"
8491 | "fetch"
8492 )
8493}
8494
8495fn extract_numeric_literal(e: &Expr) -> Option<f32> {
8496 match e {
8497 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
8498 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
8499 Expr::Unary {
8500 op: UnOp::Neg,
8501 expr,
8502 } => extract_numeric_literal(expr).map(|x| -x),
8503 _ => None,
8504 }
8505}
8506
8507pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
8515 let parts: Vec<&str> = s.split_whitespace().collect();
8516 if parts.is_empty() || !parts.len().is_multiple_of(2) {
8517 return None;
8518 }
8519 let mut months: i32 = 0;
8520 let mut micros: i64 = 0;
8521 let mut i = 0;
8522 while i < parts.len() {
8523 let n: i64 = parts[i].parse().ok()?;
8524 let unit = parts[i + 1].to_ascii_lowercase();
8525 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
8526 match unit_stripped {
8527 "microsecond" => micros = micros.checked_add(n)?,
8528 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
8529 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
8530 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
8531 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
8532 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
8533 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
8534 "month" => {
8535 let n32 = i32::try_from(n).ok()?;
8536 months = months.checked_add(n32)?;
8537 }
8538 "year" => {
8539 let n32 = i32::try_from(n).ok()?;
8540 months = months.checked_add(n32.checked_mul(12)?)?;
8541 }
8542 _ => return None,
8543 }
8544 i += 2;
8545 }
8546 Some((months, micros))
8547}
8548
8549fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
8560 Some(match ident.to_ascii_lowercase().as_str() {
8561 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
8562 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
8563 "bigint" => ColumnTypeName::BigInt,
8564 "float" | "double" | "real" => ColumnTypeName::Float,
8565 "text" => ColumnTypeName::Text,
8566 "bool" | "boolean" => ColumnTypeName::Bool,
8567 "date" => ColumnTypeName::Date,
8568 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
8569 "timestamptz" => ColumnTypeName::Timestamptz,
8570 "json" => ColumnTypeName::Json,
8571 "jsonb" => ColumnTypeName::Jsonb,
8572 "bytea" | "bytes" => ColumnTypeName::Bytes,
8573 "tsvector" => ColumnTypeName::TsVector,
8574 "tsquery" => ColumnTypeName::TsQuery,
8575 "uuid" => ColumnTypeName::Uuid,
8576 "time" => ColumnTypeName::Time,
8577 "year" => ColumnTypeName::Year,
8578 "timetz" => ColumnTypeName::TimeTz,
8579 "money" => ColumnTypeName::Money,
8580 _ => return None,
8581 })
8582}
8583
8584pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8611 parse_plpgsql_body(body)
8612}
8613
8614fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8615 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
8619 message: alloc::format!("plpgsql body lex error: {e}"),
8620 token_pos: 0,
8621 })?;
8622 let mut parser = Parser::new(tokens);
8623 parser.parse_plpgsql_block()
8624}
8625
8626#[cfg(test)]
8627mod tests {
8628 use super::*;
8629 use alloc::string::ToString;
8630
8631 fn parse(s: &str) -> Statement {
8632 parse_statement(s).expect("parse ok")
8633 }
8634
8635 fn lit_int(n: i64) -> Expr {
8636 Expr::Literal(Literal::Integer(n))
8637 }
8638
8639 fn col(name: &str) -> Expr {
8640 Expr::Column(ColumnName {
8641 qualifier: None,
8642 name: name.into(),
8643 })
8644 }
8645
8646 #[test]
8647 fn select_single_integer() {
8648 let s = parse("SELECT 1");
8649 let Statement::Select(s) = s else {
8650 panic!("expected SELECT")
8651 };
8652 assert_eq!(s.items.len(), 1);
8653 assert!(s.from.is_none());
8654 assert!(s.where_.is_none());
8655 }
8656
8657 #[test]
8658 fn select_multiple_literal_kinds() {
8659 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
8660 let Statement::Select(s) = s else {
8661 panic!("expected SELECT")
8662 };
8663 assert_eq!(s.items.len(), 5);
8664 }
8665
8666 #[test]
8667 fn select_wildcard_from_table() {
8668 let s = parse("SELECT * FROM users");
8669 let Statement::Select(s) = s else {
8670 panic!("expected SELECT")
8671 };
8672 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
8673 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
8674 }
8675
8676 #[test]
8677 fn select_with_table_alias() {
8678 let s = parse("SELECT * FROM users AS u");
8679 let Statement::Select(s) = s else {
8680 panic!("expected SELECT")
8681 };
8682 let t = &s.from.as_ref().unwrap().primary;
8683 assert_eq!(t.name, "users");
8684 assert_eq!(t.alias.as_deref(), Some("u"));
8685 }
8686
8687 #[test]
8688 fn select_with_where_eq() {
8689 let s = parse("SELECT a FROM t WHERE a = 1");
8690 let Statement::Select(s) = s else {
8691 panic!("expected SELECT")
8692 };
8693 let w = s.where_.unwrap();
8694 assert_eq!(
8695 w,
8696 Expr::Binary {
8697 lhs: Box::new(col("a")),
8698 op: BinOp::Eq,
8699 rhs: Box::new(lit_int(1)),
8700 }
8701 );
8702 }
8703
8704 #[test]
8705 fn arithmetic_precedence() {
8706 let s = parse("SELECT 1 + 2 * 3");
8707 let Statement::Select(s) = s else {
8708 panic!("expected SELECT")
8709 };
8710 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8711 panic!("wildcard?")
8712 };
8713 assert_eq!(
8714 expr,
8715 &Expr::Binary {
8716 lhs: Box::new(lit_int(1)),
8717 op: BinOp::Add,
8718 rhs: Box::new(Expr::Binary {
8719 lhs: Box::new(lit_int(2)),
8720 op: BinOp::Mul,
8721 rhs: Box::new(lit_int(3)),
8722 }),
8723 }
8724 );
8725 }
8726
8727 #[test]
8728 fn parentheses_override_precedence() {
8729 let s = parse("SELECT (1 + 2) * 3");
8730 let Statement::Select(s) = s else {
8731 panic!("expected SELECT")
8732 };
8733 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8734 panic!()
8735 };
8736 assert_eq!(
8737 expr,
8738 &Expr::Binary {
8739 lhs: Box::new(Expr::Binary {
8740 lhs: Box::new(lit_int(1)),
8741 op: BinOp::Add,
8742 rhs: Box::new(lit_int(2)),
8743 }),
8744 op: BinOp::Mul,
8745 rhs: Box::new(lit_int(3)),
8746 }
8747 );
8748 }
8749
8750 #[test]
8751 fn not_binds_below_comparison() {
8752 let s = parse("SELECT NOT a = 1 FROM t");
8754 let Statement::Select(s) = s else {
8755 panic!("expected SELECT")
8756 };
8757 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8758 panic!()
8759 };
8760 assert_eq!(
8761 expr,
8762 &Expr::Unary {
8763 op: UnOp::Not,
8764 expr: Box::new(Expr::Binary {
8765 lhs: Box::new(col("a")),
8766 op: BinOp::Eq,
8767 rhs: Box::new(lit_int(1)),
8768 }),
8769 }
8770 );
8771 }
8772
8773 #[test]
8774 fn unary_minus_binds_above_multiplication() {
8775 let s = parse("SELECT -a * 2 FROM t");
8777 let Statement::Select(s) = s else {
8778 panic!("expected SELECT")
8779 };
8780 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8781 panic!()
8782 };
8783 assert_eq!(
8784 expr,
8785 &Expr::Binary {
8786 lhs: Box::new(Expr::Unary {
8787 op: UnOp::Neg,
8788 expr: Box::new(col("a")),
8789 }),
8790 op: BinOp::Mul,
8791 rhs: Box::new(lit_int(2)),
8792 }
8793 );
8794 }
8795
8796 #[test]
8797 fn qualified_column() {
8798 let s = parse("SELECT t.col FROM t");
8799 let Statement::Select(s) = s else {
8800 panic!("expected SELECT")
8801 };
8802 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8803 panic!()
8804 };
8805 assert_eq!(
8806 expr,
8807 &Expr::Column(ColumnName {
8808 qualifier: Some("t".into()),
8809 name: "col".into()
8810 })
8811 );
8812 }
8813
8814 #[test]
8815 fn select_item_alias_with_as() {
8816 let s = parse("SELECT a AS y FROM t");
8817 let Statement::Select(s) = s else {
8818 panic!("expected SELECT")
8819 };
8820 let SelectItem::Expr { alias, .. } = &s.items[0] else {
8821 panic!()
8822 };
8823 assert_eq!(alias.as_deref(), Some("y"));
8824 }
8825
8826 #[test]
8827 fn trailing_semicolon_accepted() {
8828 let s = parse("SELECT 1;");
8829 let Statement::Select(s) = s else {
8830 panic!("expected SELECT")
8831 };
8832 assert_eq!(s.items.len(), 1);
8833 }
8834
8835 #[test]
8836 fn boolean_chain_with_and_or_not() {
8837 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
8839 let Statement::Select(s) = s else {
8840 panic!("expected SELECT")
8841 };
8842 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8843 panic!()
8844 };
8845 let expected = Expr::Binary {
8846 lhs: Box::new(Expr::Unary {
8847 op: UnOp::Not,
8848 expr: Box::new(col("a")),
8849 }),
8850 op: BinOp::Or,
8851 rhs: Box::new(Expr::Binary {
8852 lhs: Box::new(col("b")),
8853 op: BinOp::And,
8854 rhs: Box::new(Expr::Unary {
8855 op: UnOp::Not,
8856 expr: Box::new(col("c")),
8857 }),
8858 }),
8859 };
8860 assert_eq!(expr, &expected);
8861 }
8862
8863 #[test]
8864 fn empty_input_errors() {
8865 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
8872 assert!(matches!(
8873 parse_statement(" \n\t ").unwrap(),
8874 Statement::Empty
8875 ));
8876 assert!(parse_statement("SELECT FROM WHERE").is_err());
8878 }
8879
8880 #[test]
8881 fn unmatched_paren_errors() {
8882 assert!(parse_statement("SELECT (1 + 2").is_err());
8883 }
8884
8885 #[test]
8886 fn display_round_trip_simple_select() {
8887 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
8888 let text = original.to_string();
8889 let again = parse_statement(&text).expect("re-parse");
8890 assert_eq!(original, again);
8891 }
8892
8893 #[test]
8896 fn create_table_single_column() {
8897 let s = parse("CREATE TABLE foo (a INT)");
8898 let Statement::CreateTable(c) = s else {
8899 panic!("expected CreateTable")
8900 };
8901 assert_eq!(c.name, "foo");
8902 assert_eq!(c.columns.len(), 1);
8903 assert_eq!(c.columns[0].name, "a");
8904 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
8905 assert!(c.columns[0].nullable);
8906 }
8907
8908 #[test]
8909 fn create_table_multi_column_with_not_null_mix() {
8910 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
8911 let Statement::CreateTable(c) = s else {
8912 panic!()
8913 };
8914 assert_eq!(c.columns.len(), 4);
8915 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
8916 assert!(!c.columns[0].nullable);
8917 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
8918 assert!(c.columns[1].nullable);
8919 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
8920 assert!(!c.columns[2].nullable);
8921 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
8922 }
8923
8924 #[test]
8925 fn create_table_bigint_supported() {
8926 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
8927 let Statement::CreateTable(c) = s else {
8928 panic!()
8929 };
8930 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
8931 }
8932
8933 #[test]
8934 fn create_table_vector_default_is_f32() {
8935 let s = parse("CREATE TABLE t (v VECTOR(128))");
8936 let Statement::CreateTable(c) = s else {
8937 panic!()
8938 };
8939 assert_eq!(
8940 c.columns[0].ty,
8941 ColumnTypeName::Vector {
8942 dim: 128,
8943 encoding: VecEncoding::F32,
8944 },
8945 );
8946 }
8947
8948 #[test]
8949 fn create_table_vector_using_sq8() {
8950 for sql in [
8953 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
8954 "CREATE TABLE t (v VECTOR(128) using sq8)",
8955 ] {
8956 let s = parse(sql);
8957 let Statement::CreateTable(c) = s else {
8958 panic!()
8959 };
8960 assert_eq!(
8961 c.columns[0].ty,
8962 ColumnTypeName::Vector {
8963 dim: 128,
8964 encoding: VecEncoding::Sq8,
8965 },
8966 "{sql}",
8967 );
8968 }
8969 }
8970
8971 #[test]
8972 fn create_table_vector_using_unknown_errors() {
8973 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
8982 assert!(
8983 err.message.contains("USING")
8984 || err.message.contains("using")
8985 || err.message.contains("')'")
8986 || err.message.contains("','"),
8987 "expected USING/column-list rejection, got: {}",
8988 err.message
8989 );
8990 }
8991
8992 #[test]
8993 fn vector_using_sq8_display_roundtrips() {
8994 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
8997 let Statement::CreateTable(c) = s else {
8998 panic!()
8999 };
9000 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9001 }
9002
9003 #[test]
9004 fn parser_recognises_placeholders() {
9005 use crate::ast::{Expr, SelectItem, Statement};
9006 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9008 let Statement::Select(sel) = s else { panic!() };
9009 assert!(matches!(
9010 sel.items[0],
9011 SelectItem::Expr {
9012 expr: Expr::Placeholder(1),
9013 alias: None
9014 }
9015 ));
9016 let SelectItem::Expr {
9018 expr: Expr::Binary { lhs, rhs, .. },
9019 ..
9020 } = &sel.items[1]
9021 else {
9022 panic!()
9023 };
9024 assert!(matches!(**lhs, Expr::Placeholder(2)));
9025 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9026 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9028 panic!()
9029 };
9030 assert!(matches!(**rhs, Expr::Placeholder(3)));
9031 }
9032
9033 #[test]
9034 fn parser_rejects_dollar_zero() {
9035 assert!(parse_statement("SELECT $0").is_err());
9037 }
9038
9039 #[test]
9040 fn placeholder_display_roundtrips() {
9041 let s = parse("SELECT $42 FROM t");
9044 let printed = s.to_string();
9045 assert!(printed.contains("$42"));
9046 let again = parse(&printed);
9047 assert_eq!(s, again);
9048 }
9049
9050 #[test]
9051 fn alter_index_rebuild_bare() {
9052 use crate::ast::{AlterIndexTarget, Statement};
9053 let s = parse("ALTER INDEX my_idx REBUILD");
9054 let Statement::AlterIndex(a) = s else {
9055 panic!("expected AlterIndex, got {s:?}")
9056 };
9057 assert_eq!(a.name, "my_idx");
9058 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9059 }
9060
9061 #[test]
9062 fn alter_index_rebuild_with_encoding() {
9063 use crate::ast::{AlterIndexTarget, Statement};
9064 for (sql, want) in [
9065 (
9066 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9067 VecEncoding::F32,
9068 ),
9069 (
9070 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9071 VecEncoding::Sq8,
9072 ),
9073 (
9074 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9075 VecEncoding::F16,
9076 ),
9077 ] {
9078 let s = parse(sql);
9079 let Statement::AlterIndex(a) = s else {
9080 panic!("{sql}: expected AlterIndex")
9081 };
9082 assert_eq!(a.name, "my_idx");
9083 assert_eq!(
9084 a.target,
9085 AlterIndexTarget::Rebuild {
9086 encoding: Some(want)
9087 },
9088 "{sql}"
9089 );
9090 }
9091 }
9092
9093 #[test]
9094 fn alter_index_rebuild_unknown_encoding_errors() {
9095 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9096 assert!(
9097 err.message.contains("unknown vector encoding"),
9098 "got: {}",
9099 err.message
9100 );
9101 }
9102
9103 #[test]
9104 fn alter_index_rebuild_display_roundtrips() {
9105 for (input, want) in [
9106 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9107 (
9108 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9109 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9110 ),
9111 (
9112 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9113 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9114 ),
9115 ] {
9116 let s = parse(input);
9117 assert_eq!(s.to_string(), want);
9118 }
9119 }
9120
9121 #[test]
9122 fn create_table_unknown_type_errors() {
9123 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
9126 assert!(err.message.contains("unsupported column type"));
9127 }
9128
9129 #[test]
9130 fn create_table_missing_table_keyword_errors() {
9131 assert!(parse_statement("CREATE x (a INT)").is_err());
9132 }
9133
9134 #[test]
9135 fn insert_single_value() {
9136 let s = parse("INSERT INTO foo VALUES (42)");
9137 let Statement::Insert(i) = s else {
9138 panic!("expected Insert")
9139 };
9140 assert_eq!(i.table, "foo");
9141 assert_eq!(i.rows.len(), 1);
9142 assert_eq!(i.rows[0].len(), 1);
9143 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9144 }
9145
9146 #[test]
9147 fn insert_multi_value_with_mixed_literals() {
9148 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9149 let Statement::Insert(i) = s else { panic!() };
9150 assert_eq!(i.rows.len(), 1);
9151 assert_eq!(i.rows[0].len(), 5);
9152 }
9153
9154 #[test]
9155 fn insert_missing_into_errors() {
9156 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9157 }
9158
9159 #[test]
9160 fn create_table_round_trip() {
9161 let original =
9162 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9163 let text = original.to_string();
9164 let again = parse_statement(&text).expect("re-parse");
9165 assert_eq!(original, again);
9166 }
9167
9168 #[test]
9169 fn insert_round_trip_with_negation_and_string() {
9170 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9171 let text = original.to_string();
9172 let again = parse_statement(&text).expect("re-parse");
9173 assert_eq!(original, again);
9174 }
9175
9176 #[test]
9177 fn unknown_keyword_at_statement_start_errors() {
9178 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9181 assert!(err.message.contains("expected SELECT"));
9182 }
9183
9184 #[test]
9187 fn create_index_basic() {
9188 let s = parse("CREATE INDEX idx_id ON users (id)");
9189 let Statement::CreateIndex(c) = s else {
9190 panic!("expected CreateIndex")
9191 };
9192 assert_eq!(c.name, "idx_id");
9193 assert_eq!(c.table, "users");
9194 assert_eq!(c.column, "id");
9195 }
9196
9197 #[test]
9198 fn create_index_missing_on_errors() {
9199 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9200 }
9201
9202 #[test]
9203 fn create_index_missing_paren_errors() {
9204 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9205 }
9206
9207 #[test]
9208 fn create_index_round_trip() {
9209 let original = parse("CREATE INDEX by_name ON users (name)");
9210 let again = parse_statement(&original.to_string()).unwrap();
9211 assert_eq!(original, again);
9212 }
9213
9214 #[test]
9217 fn create_unique_index_basic() {
9218 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9219 let Statement::CreateIndex(c) = s else {
9220 panic!("expected CreateIndex");
9221 };
9222 assert!(c.is_unique);
9223 assert_eq!(c.column, "a");
9224 assert!(c.partial_predicate.is_none());
9225 }
9226
9227 #[test]
9228 fn create_unique_index_partial() {
9229 let s = parse(
9231 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9232 ON email_templates (user_address) WHERE is_default = true",
9233 );
9234 let Statement::CreateIndex(c) = s else {
9235 panic!("expected CreateIndex");
9236 };
9237 assert!(c.is_unique);
9238 assert_eq!(c.table, "email_templates");
9239 assert_eq!(c.column, "user_address");
9240 assert!(c.partial_predicate.is_some());
9241 }
9242
9243 #[test]
9244 fn create_unique_index_composite_with_predicate() {
9245 let s = parse(
9247 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9248 ON calendar_events (calendar_id, uid, recurrence_id) \
9249 WHERE recurrence_id IS NOT NULL",
9250 );
9251 let Statement::CreateIndex(c) = s else {
9252 panic!("expected CreateIndex");
9253 };
9254 assert!(c.is_unique);
9255 assert_eq!(c.column, "calendar_id");
9256 assert_eq!(
9257 c.extra_columns,
9258 vec!["uid".to_string(), "recurrence_id".to_string()]
9259 );
9260 assert!(c.partial_predicate.is_some());
9261 }
9262
9263 #[test]
9264 fn create_unique_index_using_btree_ok() {
9265 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9266 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9267 }
9268
9269 #[test]
9270 fn create_unique_index_using_hnsw_rejected() {
9271 let err =
9272 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9273 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9274 }
9275
9276 #[test]
9277 fn create_unique_index_round_trip() {
9278 let original = parse(
9279 "CREATE UNIQUE INDEX uq_calendar_events_master \
9280 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9281 );
9282 let again = parse_statement(&original.to_string()).unwrap();
9283 assert_eq!(original, again);
9284 }
9285
9286 #[test]
9287 fn create_unique_without_index_errors() {
9288 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9289 assert!(err.message.contains("INDEX"), "{}", err.message);
9290 }
9291
9292 #[test]
9295 fn create_table_bytea_column() {
9296 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9297 let Statement::CreateTable(c) = s else {
9298 panic!("expected CreateTable");
9299 };
9300 assert_eq!(c.columns.len(), 2);
9301 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9302 assert!(!c.columns[1].nullable);
9303 }
9304
9305 #[test]
9306 fn create_table_bytes_alias_column() {
9307 let s = parse("CREATE TABLE t (blob BYTES)");
9308 let Statement::CreateTable(c) = s else {
9309 panic!("expected CreateTable");
9310 };
9311 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9312 }
9313
9314 #[test]
9315 fn bytea_round_trip_display() {
9316 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9317 let again = parse_statement(&original.to_string()).unwrap();
9318 assert_eq!(original, again);
9319 }
9320
9321 #[test]
9324 fn begin_commit_rollback_parse_as_unit_variants() {
9325 assert_eq!(parse("BEGIN"), Statement::Begin);
9326 assert_eq!(parse("COMMIT"), Statement::Commit);
9327 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9328 assert_eq!(parse("BEGIN;"), Statement::Begin);
9330 }
9331
9332 #[test]
9335 fn inner_product_binop_parses() {
9336 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9337 let Statement::Select(s) = s else { panic!() };
9338 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9339 panic!()
9340 };
9341 assert!(matches!(
9342 expr,
9343 Expr::Binary {
9344 op: BinOp::InnerProduct,
9345 ..
9346 }
9347 ));
9348 }
9349
9350 #[test]
9351 fn cosine_distance_binop_parses() {
9352 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9353 let Statement::Select(s) = s else { panic!() };
9354 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9355 panic!()
9356 };
9357 assert!(matches!(
9358 expr,
9359 Expr::Binary {
9360 op: BinOp::CosineDistance,
9361 ..
9362 }
9363 ));
9364 }
9365
9366 #[test]
9367 fn vector_cast_postfix_wraps_string_literal() {
9368 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9369 let Statement::Select(s) = s else { panic!() };
9370 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9371 panic!()
9372 };
9373 assert!(matches!(
9374 expr,
9375 Expr::Cast {
9376 target: CastTarget::Vector,
9377 ..
9378 }
9379 ));
9380 }
9381
9382 #[test]
9383 fn unsupported_cast_target_errors() {
9384 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
9386 assert!(err.message.contains("unsupported cast target"));
9387 }
9388
9389 #[test]
9390 fn tx_statements_round_trip() {
9391 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
9392 let original = parse(q);
9393 let again = parse_statement(&original.to_string()).unwrap();
9394 assert_eq!(original, again);
9395 }
9396 }
9397
9398 #[test]
9399 fn interval_text_parsing_units() {
9400 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
9402 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
9403 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
9404 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
9405 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
9407 assert_eq!(
9408 parse_interval_text("1 day 2 hours"),
9409 Some((0, 86_400_000_000 + 7_200_000_000))
9410 );
9411 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
9413 assert_eq!(parse_interval_text(""), None);
9415 assert_eq!(parse_interval_text("garbage"), None);
9416 assert_eq!(parse_interval_text("1 fortnight"), None);
9417 assert_eq!(parse_interval_text("1"), None);
9418 }
9419
9420 #[test]
9421 fn interval_literal_roundtrips_via_display() {
9422 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
9423 let s = parsed.to_string();
9424 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
9426 let again = parse_statement(&s).unwrap();
9428 assert_eq!(parsed, again);
9429 }
9430
9431 #[test]
9434 fn parser_recognises_create_publication_bare() {
9435 let s = parse("CREATE PUBLICATION pub_a");
9436 let Statement::CreatePublication(p) = s else {
9437 panic!("expected CreatePublication, got {s:?}")
9438 };
9439 assert_eq!(p.name, "pub_a");
9440 assert_eq!(p.scope, PublicationScope::AllTables);
9441 }
9442
9443 #[test]
9444 fn parser_recognises_create_publication_for_all_tables() {
9445 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
9446 let Statement::CreatePublication(p) = s else {
9447 panic!("expected CreatePublication, got {s:?}")
9448 };
9449 assert_eq!(p.name, "pub_a");
9450 assert_eq!(p.scope, PublicationScope::AllTables);
9451 }
9452
9453 #[test]
9454 fn parser_recognises_drop_publication() {
9455 let s = parse("DROP PUBLICATION pub_a");
9456 let Statement::DropPublication(name) = s else {
9457 panic!("expected DropPublication, got {s:?}")
9458 };
9459 assert_eq!(name, "pub_a");
9460 }
9461
9462 #[test]
9463 fn parser_recognises_for_table_list() {
9464 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
9465 let Statement::CreatePublication(p) = s else {
9466 panic!("expected CreatePublication, got {s:?}")
9467 };
9468 assert_eq!(p.name, "pub_a");
9469 let PublicationScope::ForTables(ts) = p.scope else {
9470 panic!("expected ForTables scope")
9471 };
9472 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
9473 }
9474
9475 #[test]
9476 fn parser_recognises_for_tables_plural() {
9477 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
9479 let Statement::CreatePublication(p) = s else {
9480 panic!("expected CreatePublication, got {s:?}")
9481 };
9482 let PublicationScope::ForTables(ts) = p.scope else {
9483 panic!("expected ForTables")
9484 };
9485 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9486 }
9487
9488 #[test]
9489 fn parser_recognises_for_all_tables_except_list() {
9490 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
9491 let Statement::CreatePublication(p) = s else {
9492 panic!()
9493 };
9494 let PublicationScope::AllTablesExcept(ts) = p.scope else {
9495 panic!("expected AllTablesExcept")
9496 };
9497 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9498 }
9499
9500 #[test]
9501 fn parser_rejects_for_table_with_empty_list() {
9502 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
9504 .expect_err("must error on empty list");
9505 assert!(!err.message.is_empty());
9508 }
9509
9510 #[test]
9511 fn parser_recognises_show_publications() {
9512 let s = parse("SHOW PUBLICATIONS");
9515 assert!(matches!(s, Statement::ShowPublications));
9516 }
9517
9518 #[test]
9521 fn parser_recognises_create_subscription_single_publication() {
9522 let s = parse(
9523 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
9524 );
9525 let Statement::CreateSubscription(c) = s else {
9526 panic!("expected CreateSubscription, got {s:?}")
9527 };
9528 assert_eq!(c.name, "sub_a");
9529 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
9530 assert_eq!(c.publications, alloc::vec!["pub_a"]);
9531 }
9532
9533 #[test]
9534 fn parser_recognises_create_subscription_multi_publication() {
9535 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
9536 let Statement::CreateSubscription(c) = s else {
9537 panic!()
9538 };
9539 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
9540 }
9541
9542 #[test]
9543 fn parser_rejects_create_subscription_missing_connection() {
9544 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
9545 .expect_err("must error on missing CONNECTION");
9546 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
9547 }
9548
9549 #[test]
9550 fn parser_rejects_create_subscription_missing_publication() {
9551 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
9552 .expect_err("must error on missing PUBLICATION");
9553 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
9554 }
9555
9556 #[test]
9557 fn parser_recognises_drop_subscription() {
9558 let s = parse("DROP SUBSCRIPTION sub_a");
9559 let Statement::DropSubscription(name) = s else {
9560 panic!("expected DropSubscription, got {s:?}")
9561 };
9562 assert_eq!(name, "sub_a");
9563 }
9564
9565 #[test]
9566 fn parser_recognises_show_subscriptions() {
9567 let s = parse("SHOW SUBSCRIPTIONS");
9568 assert!(matches!(s, Statement::ShowSubscriptions));
9569 }
9570
9571 #[test]
9572 fn parser_recognises_wait_for_wal_position_no_timeout() {
9573 let s = parse("WAIT FOR WAL POSITION 12345");
9574 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9575 panic!("expected WaitForWalPosition, got {s:?}")
9576 };
9577 assert_eq!(pos, 12345);
9578 assert!(timeout_ms.is_none());
9579 }
9580
9581 #[test]
9582 fn parser_recognises_wait_for_wal_position_with_timeout() {
9583 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
9584 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9585 panic!()
9586 };
9587 assert_eq!(pos, 67890);
9588 assert_eq!(timeout_ms, Some(5000));
9589 }
9590
9591 #[test]
9592 fn parser_rejects_wait_with_negative_position() {
9593 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
9599 assert!(!err.message.is_empty());
9600 }
9601
9602 #[test]
9603 fn parser_recognises_bare_analyze() {
9604 let s = parse("ANALYZE");
9605 assert!(matches!(s, Statement::Analyze(None)));
9606 }
9607
9608 #[test]
9609 fn parser_recognises_analyze_with_table() {
9610 let s = parse("ANALYZE users");
9611 let Statement::Analyze(Some(name)) = s else {
9612 panic!("expected Analyze, got {s:?}")
9613 };
9614 assert_eq!(name, "users");
9615 }
9616
9617 #[test]
9618 fn parser_recognises_analyze_with_quoted_table() {
9619 let s = parse("ANALYZE \"Mixed Case\"");
9620 let Statement::Analyze(Some(name)) = s else {
9621 panic!()
9622 };
9623 assert_eq!(name, "Mixed Case");
9624 }
9625
9626 #[test]
9627 fn parser_rejects_analyze_with_garbage_token() {
9628 let err = parse_statement("ANALYZE 42").expect_err("must error");
9629 assert!(!err.message.is_empty());
9630 }
9631
9632 #[test]
9633 fn analyze_display_roundtrips() {
9634 for sql in ["ANALYZE", "ANALYZE users"] {
9635 let s = parse(sql);
9636 let printed = s.to_string();
9637 let again = parse_statement(&printed)
9638 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9639 assert_eq!(s, again);
9640 }
9641 }
9642
9643 #[test]
9644 fn wait_for_display_roundtrips() {
9645 for sql in [
9646 "WAIT FOR WAL POSITION 12345",
9647 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
9648 ] {
9649 let s = parse(sql);
9650 let printed = s.to_string();
9651 let again = parse_statement(&printed)
9652 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9653 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9654 }
9655 }
9656
9657 #[test]
9658 fn subscription_ddl_display_roundtrips() {
9659 for sql in [
9660 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
9661 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
9662 "DROP SUBSCRIPTION sub_a",
9663 "SHOW SUBSCRIPTIONS",
9664 ] {
9665 let s = parse(sql);
9666 let printed = s.to_string();
9667 let again = parse_statement(&printed)
9668 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9669 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9670 }
9671 }
9672
9673 #[test]
9674 fn parser_drop_dispatches_user_vs_publication() {
9675 let s = parse("DROP USER 'alice'");
9678 let Statement::DropUser(name) = s else {
9679 panic!("expected DropUser, got {s:?}")
9680 };
9681 assert_eq!(name, "alice");
9682 let s = parse("DROP PUBLICATION p1");
9684 assert!(matches!(s, Statement::DropPublication(_)));
9685 }
9686
9687 #[test]
9688 fn publication_ddl_display_roundtrips() {
9689 for sql in [
9692 "CREATE PUBLICATION pub_a",
9693 "CREATE PUBLICATION pub_a FOR ALL TABLES",
9694 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
9695 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
9696 "DROP PUBLICATION pub_a",
9697 "SHOW PUBLICATIONS",
9698 ] {
9699 let s = parse(sql);
9700 let printed = s.to_string();
9701 let again = parse_statement(&printed)
9702 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9703 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9704 }
9705 }
9706
9707 #[test]
9710 fn create_function_returns_trigger_plpgsql_minimal() {
9711 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
9712 let s = parse(sql);
9713 let Statement::CreateFunction(f) = s else {
9714 panic!("expected CreateFunction");
9715 };
9716 assert_eq!(f.name, "noop");
9717 assert!(!f.or_replace);
9718 assert!(f.args.is_empty());
9719 assert!(matches!(f.returns, FunctionReturn::Trigger));
9720 assert_eq!(f.language, "plpgsql");
9721 let FunctionBody::PlPgSql(block) = f.body else {
9722 panic!("expected PlPgSql body");
9723 };
9724 assert_eq!(block.statements.len(), 1);
9725 assert!(matches!(
9726 block.statements[0],
9727 PlPgSqlStmt::Return(ReturnTarget::New)
9728 ));
9729 }
9730
9731 #[test]
9732 fn create_function_or_replace_with_assignment() {
9733 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
9736BEGIN
9737 NEW.search_vector := to_tsvector('english', NEW.subject);
9738 RETURN NEW;
9739END;
9740$$";
9741 let s = parse(sql);
9742 let Statement::CreateFunction(f) = s else {
9743 panic!("expected CreateFunction");
9744 };
9745 assert!(f.or_replace);
9746 let FunctionBody::PlPgSql(block) = &f.body else {
9747 panic!("expected PlPgSql body");
9748 };
9749 assert_eq!(block.statements.len(), 2);
9750 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
9752 panic!("expected Assign as first stmt");
9753 };
9754 match target {
9755 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
9756 other => panic!("expected NEW.col, got {other:?}"),
9757 }
9758 assert!(matches!(
9760 block.statements[1],
9761 PlPgSqlStmt::Return(ReturnTarget::New)
9762 ));
9763 }
9764
9765 #[test]
9766 fn create_trigger_after_insert_or_update() {
9767 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
9768 let s = parse(sql);
9769 let Statement::CreateTrigger(t) = s else {
9770 panic!("expected CreateTrigger");
9771 };
9772 assert_eq!(t.name, "tg");
9773 assert_eq!(t.table, "messages");
9774 assert_eq!(t.timing, TriggerTiming::After);
9775 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
9776 assert_eq!(t.for_each, TriggerForEach::Row);
9777 assert_eq!(t.function, "update_sv");
9778 }
9779
9780 #[test]
9781 fn create_trigger_before_delete_execute_procedure_alias() {
9782 let sql =
9784 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
9785 let s = parse(sql);
9786 let Statement::CreateTrigger(t) = s else {
9787 panic!("expected CreateTrigger");
9788 };
9789 assert_eq!(t.timing, TriggerTiming::Before);
9790 assert_eq!(t.events, vec![TriggerEvent::Delete]);
9791 }
9792
9793 #[test]
9794 fn drop_trigger_if_exists_round_trips() {
9795 let s = Statement::DropTrigger {
9800 name: "tg".into(),
9801 table: "messages".into(),
9802 if_exists: true,
9803 };
9804 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
9805 }
9806
9807 #[test]
9808 fn trigger_ddl_display_roundtrips_through_parser() {
9809 for sql in [
9813 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
9814 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
9815 ] {
9816 let s = parse(sql);
9817 let printed = s.to_string();
9818 let again = parse_statement(&printed)
9819 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9820 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9821 }
9822 }
9823}