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 if let Token::Ident(s) = self.peek()
6147 && [
6148 "unique",
6149 "primary",
6150 "foreign",
6151 "constraint",
6152 "check",
6153 "references",
6154 "exclude",
6155 ]
6156 .iter()
6157 .any(|kw| s.eq_ignore_ascii_case(kw))
6158 {
6159 return Err(self.err(alloc::format!(
6160 "unexpected reserved keyword '{s}' at start of column definition \
6161 (malformed table constraint?)"
6162 )));
6163 }
6164 let name = self.expect_ident_like()?;
6165 let (
6166 ty,
6167 implied_auto_increment,
6168 implied_not_null,
6169 user_type_ref,
6170 collation,
6171 is_unsigned,
6172 inline_enum_variants,
6173 inline_set_variants,
6174 ) = self.parse_type_with_implied_flags()?;
6175 let mut default: Option<Expr> = None;
6179 let mut nullable = !implied_not_null;
6180 let mut nullability_seen = implied_not_null;
6181 let mut auto_increment = implied_auto_increment;
6182 let mut is_primary_key = false;
6183 let mut is_unique = false;
6184 let mut check: Option<Expr> = None;
6185 let mut on_update_runtime: Option<Expr> = None;
6186 loop {
6187 if matches!(self.peek(), Token::On)
6192 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6193 {
6194 self.advance(); self.advance(); let next = self.peek().clone();
6198 match next {
6199 Token::Ident(s) | Token::QuotedIdent(s)
6200 if s.eq_ignore_ascii_case("current_timestamp") =>
6201 {
6202 self.advance();
6203 if matches!(self.peek(), Token::LParen) {
6205 self.advance();
6206 if !matches!(self.peek(), Token::Integer(_)) {
6207 return Err(self.err(alloc::format!(
6208 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6209 self.peek()
6210 )));
6211 }
6212 self.advance();
6213 if !matches!(self.peek(), Token::RParen) {
6214 return Err(self.err(alloc::format!(
6215 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6216 self.peek()
6217 )));
6218 }
6219 self.advance();
6220 }
6221 on_update_runtime = Some(Expr::FunctionCall {
6222 name: "now".into(),
6223 args: Vec::new(),
6224 });
6225 continue;
6226 }
6227 other => {
6228 return Err(self.err(alloc::format!(
6229 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6230 )));
6231 }
6232 }
6233 }
6234 if matches!(self.peek(), Token::Default) {
6235 if default.is_some() {
6236 return Err(self.err("DEFAULT specified twice".into()));
6237 }
6238 self.advance();
6239 default = Some(self.parse_expr(0)?);
6240 continue;
6241 }
6242 if matches!(self.peek(), Token::Not) {
6243 if nullability_seen {
6244 return Err(self.err("NOT NULL specified twice".into()));
6245 }
6246 self.advance();
6247 if !matches!(self.peek(), Token::Null) {
6248 return Err(self.err(format!(
6249 "expected NULL after NOT in column def, got {:?}",
6250 self.peek()
6251 )));
6252 }
6253 self.advance();
6254 nullable = false;
6255 nullability_seen = true;
6256 continue;
6257 }
6258 if matches!(self.peek(), Token::Null) {
6264 if nullability_seen && !nullable {
6265 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6266 }
6267 self.advance();
6268 nullable = true;
6269 nullability_seen = true;
6270 continue;
6271 }
6272 if let Token::Ident(s) = self.peek()
6275 && (s.eq_ignore_ascii_case("auto_increment")
6276 || s.eq_ignore_ascii_case("autoincrement"))
6277 {
6278 if auto_increment {
6279 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6280 }
6281 self.advance();
6282 auto_increment = true;
6283 continue;
6284 }
6285 if let Token::Ident(s) = self.peek()
6290 && s.eq_ignore_ascii_case("primary")
6291 {
6292 if is_primary_key {
6293 return Err(self.err("PRIMARY KEY specified twice".into()));
6294 }
6295 let next = self.tokens.get(self.pos + 1);
6297 let next_is_key = matches!(
6298 next,
6299 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6300 );
6301 if !next_is_key {
6302 return Err(self.err(format!(
6303 "expected KEY after PRIMARY in column def, got {:?}",
6304 next
6305 )));
6306 }
6307 self.advance(); self.advance(); is_primary_key = true;
6310 if nullability_seen && nullable {
6311 return Err(self.err(
6312 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6313 ));
6314 }
6315 nullable = false;
6316 nullability_seen = true;
6317 continue;
6318 }
6319 if let Token::Ident(s) = self.peek()
6323 && s.eq_ignore_ascii_case("unique")
6324 {
6325 if is_unique {
6326 return Err(self.err("UNIQUE specified twice".into()));
6327 }
6328 self.advance();
6329 is_unique = true;
6330 continue;
6331 }
6332 if let Token::Ident(s) = self.peek()
6337 && s.eq_ignore_ascii_case("check")
6338 {
6339 self.advance();
6340 if !matches!(self.peek(), Token::LParen) {
6341 return Err(self.err(alloc::format!(
6342 "expected '(' after CHECK in column def, got {:?}",
6343 self.peek()
6344 )));
6345 }
6346 self.advance();
6347 let pred = self.parse_expr(0)?;
6348 if !matches!(self.peek(), Token::RParen) {
6349 return Err(self.err(alloc::format!(
6350 "expected ')' to close CHECK predicate, got {:?}",
6351 self.peek()
6352 )));
6353 }
6354 self.advance();
6355 check = Some(match check.take() {
6356 Some(prev) => Expr::Binary {
6357 op: BinOp::And,
6358 lhs: Box::new(prev),
6359 rhs: Box::new(pred),
6360 },
6361 None => pred,
6362 });
6363 continue;
6364 }
6365 break;
6366 }
6367 Ok(ColumnDef {
6368 name,
6369 ty,
6370 nullable,
6371 default,
6372 auto_increment,
6373 is_primary_key,
6374 is_unique,
6375 check,
6376 user_type_ref,
6377 on_update_runtime,
6378 collation,
6379 is_unsigned,
6380 inline_enum_variants,
6381 inline_set_variants,
6382 })
6383 }
6384
6385 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6389 if !matches!(self.peek(), Token::LParen) {
6390 return Ok((0, 0));
6394 }
6395 self.advance();
6396 let precision = match self.advance() {
6397 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6398 other => {
6399 return Err(ParseError {
6400 message: format!(
6401 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6402 ),
6403 token_pos: self.pos.saturating_sub(1),
6404 });
6405 }
6406 };
6407 let scale = if matches!(self.peek(), Token::Comma) {
6408 self.advance();
6409 match self.advance() {
6410 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6411 u8::try_from(n).expect("range-checked")
6412 }
6413 other => {
6414 return Err(ParseError {
6415 message: format!(
6416 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6417 ),
6418 token_pos: self.pos.saturating_sub(1),
6419 });
6420 }
6421 }
6422 } else {
6423 0
6424 };
6425 if !matches!(self.peek(), Token::RParen) {
6426 return Err(self.err(format!(
6427 "expected ')' to close NUMERIC params, got {:?}",
6428 self.peek()
6429 )));
6430 }
6431 self.advance();
6432 Ok((precision, scale))
6433 }
6434
6435 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6443 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6444 return Ok(VecEncoding::F32);
6445 }
6446 let n1 = self.tokens.get(self.pos + 1);
6452 let next_is_encoding = matches!(
6453 n1,
6454 Some(Token::Ident(s))
6455 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6456 );
6457 if !next_is_encoding {
6458 return Ok(VecEncoding::F32);
6459 }
6460 self.advance();
6461 let enc_ident = match self.advance() {
6462 Token::Ident(s) => s,
6463 other => {
6464 return Err(self.err(format!(
6465 "expected vector encoding after USING, got {other:?}"
6466 )));
6467 }
6468 };
6469 match enc_ident.to_ascii_lowercase().as_str() {
6470 "sq8" => Ok(VecEncoding::Sq8),
6471 "half" => Ok(VecEncoding::F16),
6474 other => Err(self.err(format!(
6475 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6476 ))),
6477 }
6478 }
6479
6480 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6486 if !matches!(self.peek(), Token::LParen) {
6487 return None;
6488 }
6489 let next = self.tokens.get(self.pos + 1)?;
6490 let n = match next {
6491 Token::Integer(n) => *n,
6492 _ => return None,
6493 };
6494 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6495 return None;
6496 }
6497 Some(n)
6498 }
6499
6500 fn consume_optional_paren_size(&mut self) {
6504 if !matches!(self.peek(), Token::LParen) {
6505 return;
6506 }
6507 self.advance();
6508 let mut depth = 1usize;
6510 while depth > 0 {
6511 match self.peek() {
6512 Token::LParen => depth += 1,
6513 Token::RParen => depth -= 1,
6514 Token::Eof => return,
6515 _ => {}
6516 }
6517 self.advance();
6518 }
6519 }
6520
6521 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6522 if !matches!(self.peek(), Token::LParen) {
6523 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6524 }
6525 self.advance();
6526 let n = match self.advance() {
6527 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6528 message: format!("{label} size too large: {n}"),
6529 token_pos: self.pos.saturating_sub(1),
6530 })?,
6531 other => {
6532 return Err(ParseError {
6533 message: format!("expected positive integer {label} size, got {other:?}"),
6534 token_pos: self.pos.saturating_sub(1),
6535 });
6536 }
6537 };
6538 if !matches!(self.peek(), Token::RParen) {
6539 return Err(self.err(format!(
6540 "expected ')' after {label} size, got {:?}",
6541 self.peek()
6542 )));
6543 }
6544 self.advance();
6545 Ok(n)
6546 }
6547
6548 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6549 debug_assert!(matches!(self.peek(), Token::Insert));
6550 self.advance();
6551 if !matches!(self.peek(), Token::Into) {
6552 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6553 }
6554 self.advance();
6555 let table = self.expect_ident_like()?;
6556 let columns = if matches!(self.peek(), Token::LParen) {
6558 self.advance();
6559 let mut names = Vec::new();
6560 loop {
6561 names.push(self.expect_ident_like()?);
6562 match self.peek() {
6563 Token::Comma => {
6564 self.advance();
6565 }
6566 Token::RParen => {
6567 self.advance();
6568 break;
6569 }
6570 other => {
6571 return Err(self.err(format!(
6572 "expected ',' or ')' in INSERT column list, got {other:?}"
6573 )));
6574 }
6575 }
6576 }
6577 Some(names)
6578 } else {
6579 None
6580 };
6581 if matches!(self.peek(), Token::Select) {
6584 let select_stmt = match self.parse_select_stmt()? {
6585 Statement::Select(s) => s,
6586 other => {
6587 return Err(self.err(alloc::format!(
6588 "expected SELECT after INSERT INTO ... target, got {other:?}"
6589 )));
6590 }
6591 };
6592 let on_conflict = self.parse_optional_on_conflict()?;
6593 let returning = self.parse_optional_returning()?;
6594 return Ok(Statement::Insert(InsertStatement {
6595 table,
6596 columns,
6597 rows: Vec::new(),
6598 select_source: Some(Box::new(select_stmt)),
6599 on_conflict,
6600 returning,
6601 }));
6602 }
6603 if !matches!(self.peek(), Token::Values) {
6604 return Err(self.err(format!(
6605 "expected VALUES or SELECT after table name, got {:?}",
6606 self.peek()
6607 )));
6608 }
6609 self.advance();
6610 if !matches!(self.peek(), Token::LParen) {
6611 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
6612 }
6613 let mut rows = Vec::new();
6614 loop {
6615 if !matches!(self.peek(), Token::LParen) {
6617 return Err(self.err(format!(
6618 "expected '(' for next VALUES tuple, got {:?}",
6619 self.peek()
6620 )));
6621 }
6622 self.advance();
6623 let mut tuple = Vec::new();
6624 loop {
6625 tuple.push(self.parse_expr(0)?);
6626 match self.peek() {
6627 Token::Comma => {
6628 self.advance();
6629 }
6630 Token::RParen => {
6631 self.advance();
6632 break;
6633 }
6634 other => {
6635 return Err(self.err(format!(
6636 "expected ',' or ')' in VALUES tuple, got {other:?}"
6637 )));
6638 }
6639 }
6640 }
6641 if tuple.is_empty() {
6642 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
6643 }
6644 rows.push(tuple);
6645 if matches!(self.peek(), Token::Comma) {
6647 self.advance();
6648 } else {
6649 break;
6650 }
6651 }
6652 let on_conflict = self.parse_optional_on_conflict()?;
6653 let returning = self.parse_optional_returning()?;
6654 Ok(Statement::Insert(InsertStatement {
6655 table,
6656 columns,
6657 rows,
6658 select_source: None,
6659 on_conflict,
6660 returning,
6661 }))
6662 }
6663
6664 fn parse_optional_on_conflict(
6669 &mut self,
6670 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
6671 if !matches!(self.peek(), Token::On) {
6672 return Ok(None);
6673 }
6674 let next_is_conflict = matches!(
6677 self.tokens.get(self.pos + 1),
6678 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
6679 );
6680 if !next_is_conflict {
6681 return Ok(None);
6682 }
6683 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
6687 if matches!(self.peek(), Token::LParen) {
6688 self.advance();
6689 loop {
6690 target_columns.push(self.expect_ident_like()?);
6691 match self.peek() {
6692 Token::Comma => {
6693 self.advance();
6694 }
6695 Token::RParen => {
6696 self.advance();
6697 break;
6698 }
6699 other => {
6700 return Err(self.err(alloc::format!(
6701 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
6702 )));
6703 }
6704 }
6705 }
6706 }
6707 match self.advance() {
6709 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
6710 other => {
6711 return Err(self.err(alloc::format!(
6712 "expected DO after ON CONFLICT [(…)], got {other:?}"
6713 )));
6714 }
6715 }
6716 let action = match self.advance() {
6718 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
6719 crate::ast::OnConflictAction::Nothing
6720 }
6721 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
6722 self.parse_on_conflict_update_action()?
6723 }
6724 other => {
6725 return Err(self.err(alloc::format!(
6726 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
6727 )));
6728 }
6729 };
6730 Ok(Some(crate::ast::OnConflictClause {
6731 target_columns,
6732 action,
6733 }))
6734 }
6735
6736 fn parse_on_conflict_update_action(
6740 &mut self,
6741 ) -> Result<crate::ast::OnConflictAction, ParseError> {
6742 match self.advance() {
6744 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
6745 other => {
6746 return Err(self.err(alloc::format!(
6747 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
6748 )));
6749 }
6750 }
6751 let mut assignments: Vec<(String, Expr)> = Vec::new();
6752 loop {
6753 let col = self.expect_ident_like()?;
6754 if !matches!(self.peek(), Token::Eq) {
6755 return Err(self.err(alloc::format!(
6756 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
6757 self.peek()
6758 )));
6759 }
6760 self.advance();
6761 let value = self.parse_expr(0)?;
6762 assignments.push((col, value));
6763 if matches!(self.peek(), Token::Comma) {
6764 self.advance();
6765 continue;
6766 }
6767 break;
6768 }
6769 let where_ = if matches!(self.peek(), Token::Where) {
6770 self.advance();
6771 Some(self.parse_expr(0)?)
6772 } else {
6773 None
6774 };
6775 Ok(crate::ast::OnConflictAction::Update {
6776 assignments,
6777 where_,
6778 })
6779 }
6780
6781 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
6782 let mut items = Vec::new();
6783 loop {
6784 items.push(self.parse_select_item()?);
6785 if matches!(self.peek(), Token::Comma) {
6786 self.advance();
6787 } else {
6788 break;
6789 }
6790 }
6791 Ok(items)
6792 }
6793
6794 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
6795 if matches!(self.peek(), Token::Star) {
6796 self.advance();
6797 return Ok(SelectItem::Wildcard);
6798 }
6799 let expr = self.parse_expr(0)?;
6800 let alias = self.parse_optional_alias();
6801 Ok(SelectItem::Expr { expr, alias })
6802 }
6803
6804 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
6805 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
6811 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6812 {
6813 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
6817 Statement::Select(s) => s,
6818 other => {
6819 return Err(self.err(alloc::format!(
6820 "expected SELECT inside LATERAL ( … ), got {other:?}"
6821 )));
6822 }
6823 };
6824 if !matches!(self.peek(), Token::RParen) {
6825 return Err(self.err(alloc::format!(
6826 "expected ')' after LATERAL subquery, got {:?}",
6827 self.peek()
6828 )));
6829 }
6830 self.advance();
6831 let alias_ident = self.parse_optional_alias();
6832 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
6833 return Ok(TableRef {
6834 name,
6835 alias: alias_ident,
6836 as_of_segment: None,
6837 unnest_expr: None,
6838 unnest_column_aliases: Vec::new(),
6839 generate_series_args: None,
6840 lateral_subquery: Some(Box::new(inner)),
6841 });
6842 }
6843 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
6847 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6848 {
6849 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
6852 if !matches!(self.peek(), Token::RParen) {
6853 return Err(self.err(alloc::format!(
6854 "expected ')' after unnest() argument, got {:?}",
6855 self.peek()
6856 )));
6857 }
6858 self.advance();
6859 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
6860 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
6861 return Ok(TableRef {
6862 name,
6863 alias: alias_ident,
6864 as_of_segment: None,
6865 unnest_expr: Some(Box::new(expr)),
6866 unnest_column_aliases,
6867 generate_series_args: None,
6868 lateral_subquery: None,
6869 });
6870 }
6871 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
6881 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6882 {
6883 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
6886 loop {
6887 args.push(self.parse_expr(0)?);
6888 if matches!(self.peek(), Token::Comma) {
6889 self.advance();
6890 continue;
6891 }
6892 break;
6893 }
6894 if !matches!(self.peek(), Token::RParen) {
6895 return Err(self.err(alloc::format!(
6896 "expected ')' after generate_series() arguments, got {:?}",
6897 self.peek()
6898 )));
6899 }
6900 self.advance();
6901 if args.len() < 2 || args.len() > 3 {
6902 return Err(self.err(alloc::format!(
6903 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
6904 args.len()
6905 )));
6906 }
6907 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
6908 let name = alias_ident
6909 .clone()
6910 .unwrap_or_else(|| "generate_series".to_string());
6911 return Ok(TableRef {
6912 name,
6913 alias: alias_ident,
6914 as_of_segment: None,
6915 unnest_expr: None,
6916 unnest_column_aliases: Vec::new(),
6917 generate_series_args: Some(args),
6918 lateral_subquery: None,
6919 });
6920 }
6921 let name = if let Some(synth) = self.try_peek_meta_qualified() {
6930 synth
6931 } else {
6932 self.expect_ident_like()?
6933 };
6934 let as_of_segment = if matches!(self.peek(), Token::As)
6940 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
6941 {
6942 self.advance(); self.advance(); let kw = match self.peek().clone() {
6945 Token::Ident(s) | Token::QuotedIdent(s) => s,
6946 other => {
6947 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
6948 }
6949 };
6950 if !kw.eq_ignore_ascii_case("segment") {
6951 return Err(self.err(format!(
6952 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
6953 )));
6954 }
6955 self.advance();
6956 let id = match self.advance() {
6959 Token::String(s) => s
6960 .parse::<u32>()
6961 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
6962 Token::Integer(n) => u32::try_from(n)
6963 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
6964 other => {
6965 return Err(self.err(format!(
6966 "expected segment id literal after AS OF SEGMENT, got {other:?}"
6967 )));
6968 }
6969 };
6970 Some(id)
6971 } else {
6972 None
6973 };
6974 let alias = self.parse_optional_alias();
6975 Ok(TableRef {
6976 name,
6977 alias,
6978 as_of_segment,
6979 unnest_expr: None,
6980 unnest_column_aliases: Vec::new(),
6981 generate_series_args: None,
6982 lateral_subquery: None,
6983 })
6984 }
6985
6986 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
6992 let alias = self.parse_optional_alias();
6993 if alias.is_none() {
6994 return (None, Vec::new());
6995 }
6996 let mut cols: Vec<String> = Vec::new();
6997 if matches!(self.peek(), Token::LParen) {
6998 self.advance();
6999 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7000 self.advance();
7001 cols.push(s);
7002 if matches!(self.peek(), Token::Comma) {
7003 self.advance();
7004 continue;
7005 }
7006 break;
7007 }
7008 if matches!(self.peek(), Token::RParen) {
7009 self.advance();
7010 }
7011 }
7012 (alias, cols)
7013 }
7014
7015 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7020 let primary = self.parse_table_ref()?;
7021 let mut joins = Vec::new();
7022 loop {
7023 if matches!(self.peek(), Token::Comma) {
7025 self.advance();
7026 let table = self.parse_table_ref()?;
7027 joins.push(FromJoin {
7028 kind: JoinKind::Cross,
7029 table,
7030 on: None,
7031 });
7032 continue;
7033 }
7034 let kind =
7037 match self.peek() {
7038 Token::Inner => {
7039 self.advance();
7040 if !matches!(self.peek(), Token::Join) {
7041 return Err(self
7042 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7043 }
7044 self.advance();
7045 JoinKind::Inner
7046 }
7047 Token::Left => {
7048 self.advance();
7049 if matches!(self.peek(), Token::Outer) {
7050 self.advance();
7051 }
7052 if !matches!(self.peek(), Token::Join) {
7053 return Err(self.err(format!(
7054 "expected JOIN after LEFT [OUTER], got {:?}",
7055 self.peek()
7056 )));
7057 }
7058 self.advance();
7059 JoinKind::Left
7060 }
7061 Token::Cross => {
7062 self.advance();
7063 if !matches!(self.peek(), Token::Join) {
7064 return Err(self
7065 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7066 }
7067 self.advance();
7068 JoinKind::Cross
7069 }
7070 Token::Join => {
7071 self.advance();
7072 JoinKind::Inner
7073 }
7074 _ => break,
7075 };
7076 let table = self.parse_table_ref()?;
7077 let on = if matches!(self.peek(), Token::On) {
7078 self.advance();
7079 Some(self.parse_expr(0)?)
7080 } else if kind == JoinKind::Cross {
7081 None
7082 } else {
7083 return Err(self.err(format!(
7084 "expected ON after {:?} JOIN, got {:?}",
7085 kind,
7086 self.peek()
7087 )));
7088 };
7089 joins.push(FromJoin { kind, table, on });
7090 }
7091 Ok(FromClause { primary, joins })
7092 }
7093
7094 fn parse_optional_alias(&mut self) -> Option<String> {
7099 if matches!(self.peek(), Token::As) {
7100 self.advance();
7101 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7106 return self.expect_ident_like().ok();
7107 }
7108 return None;
7109 }
7110 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7118 if is_alias_stopword(s) {
7119 return None;
7120 }
7121 return self.expect_ident_like().ok();
7122 }
7123 None
7124 }
7125
7126 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7128 let mut lhs = self.parse_unary()?;
7129 while let Some((op, prec)) = binop_from(self.peek()) {
7130 if prec < min_prec {
7131 break;
7132 }
7133 self.advance();
7134 let any_kind = match self.peek() {
7139 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7140 Some(false)
7141 }
7142 Token::Ident(s) | Token::QuotedIdent(s)
7143 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7144 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7145 {
7146 Some(s.eq_ignore_ascii_case("any"))
7147 }
7148 _ => None,
7149 };
7150 if let Some(is_any) = any_kind {
7151 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7154 if !matches!(self.peek(), Token::RParen) {
7155 return Err(self.err(alloc::format!(
7156 "expected ')' after ANY/ALL argument, got {:?}",
7157 self.peek()
7158 )));
7159 }
7160 self.advance();
7161 lhs = Expr::AnyAll {
7162 expr: Box::new(lhs),
7163 op,
7164 array: Box::new(arr),
7165 is_any,
7166 };
7167 continue;
7168 }
7169 let rhs = self.parse_expr(prec + 1)?;
7170 lhs = Expr::Binary {
7171 lhs: Box::new(lhs),
7172 op,
7173 rhs: Box::new(rhs),
7174 };
7175 }
7176 Ok(lhs)
7177 }
7178
7179 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7180 match self.peek() {
7181 Token::Not => {
7182 self.advance();
7183 let e = self.parse_expr(3)?;
7186 Ok(Expr::Unary {
7187 op: UnOp::Not,
7188 expr: Box::new(e),
7189 })
7190 }
7191 Token::Minus => {
7192 self.advance();
7193 let e = self.parse_expr(8)?;
7196 Ok(Expr::Unary {
7197 op: UnOp::Neg,
7198 expr: Box::new(e),
7199 })
7200 }
7201 _ => self.parse_atom(),
7202 }
7203 }
7204
7205 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7206 let tok_pos = self.pos;
7207 match self.advance() {
7208 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7209 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7210 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7211 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7212 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7213 Token::Null => Ok(Expr::Literal(Literal::Null)),
7214 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7218 Token::LParen => {
7219 if matches!(self.peek(), Token::Select) {
7223 let inner = self.parse_select_stmt()?;
7224 match self.advance() {
7225 Token::RParen => {
7226 let Statement::Select(s) = inner else {
7227 unreachable!("parse_select_stmt returns Select")
7228 };
7229 Ok(Expr::ScalarSubquery(Box::new(s)))
7230 }
7231 other => Err(ParseError {
7232 message: format!("expected ')' after scalar subquery, got {other:?}"),
7233 token_pos: self.pos.saturating_sub(1),
7234 }),
7235 }
7236 } else {
7237 let e = self.parse_expr(0)?;
7238 match self.advance() {
7239 Token::RParen => Ok(e),
7240 other => Err(ParseError {
7241 message: format!("expected ')', got {other:?}"),
7242 token_pos: self.pos.saturating_sub(1),
7243 }),
7244 }
7245 }
7246 }
7247 Token::LBracket => self.parse_vector_literal_body(),
7248 Token::Extract => self.parse_extract_atom(),
7249 Token::Interval => self.parse_interval_atom(),
7250 Token::Left if matches!(self.peek(), Token::LParen) => {
7257 self.advance(); let mut args = Vec::new();
7259 if !matches!(self.peek(), Token::RParen) {
7260 loop {
7261 args.push(self.parse_expr(0)?);
7262 match self.peek() {
7263 Token::Comma => {
7264 self.advance();
7265 }
7266 Token::RParen => break,
7267 other => {
7268 return Err(self.err(alloc::format!(
7269 "expected ',' or ')' in left() args, got {other:?}"
7270 )));
7271 }
7272 }
7273 }
7274 }
7275 self.advance(); Ok(Expr::FunctionCall {
7277 name: "left".into(),
7278 args,
7279 })
7280 }
7281 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7286 self.parse_exists_atom(false)
7287 }
7288 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7292 self.parse_case_atom()
7293 }
7294 Token::Ident(s) | Token::QuotedIdent(s)
7298 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7299 {
7300 self.advance(); let mut items: Vec<Expr> = Vec::new();
7302 if !matches!(self.peek(), Token::RBracket) {
7303 loop {
7304 items.push(self.parse_expr(0)?);
7305 match self.peek() {
7306 Token::Comma => {
7307 self.advance();
7308 }
7309 Token::RBracket => break,
7310 other => {
7311 return Err(self.err(alloc::format!(
7312 "expected ',' or ']' in ARRAY literal, got {other:?}"
7313 )));
7314 }
7315 }
7316 }
7317 }
7318 self.advance(); Ok(Expr::Array(items))
7320 }
7321 Token::Ident(s) | Token::QuotedIdent(s)
7336 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7337 {
7338 self.parse_match_against_atom()
7339 }
7340 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7341 other => Err(ParseError {
7342 message: format!("unexpected token {other:?} in expression"),
7343 token_pos: tok_pos,
7344 }),
7345 }
7346 .and_then(|atom| self.finish_postfix_casts(atom))
7348 }
7349
7350 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7353 loop {
7354 if matches!(self.peek(), Token::DoubleColon) {
7355 self.advance();
7356 let target = match self.advance() {
7361 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7362 "int" | "integer" | "int4" => {
7363 if matches!(self.peek(), Token::LBracket)
7364 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7365 {
7366 self.advance();
7367 self.advance();
7368 CastTarget::IntArray
7369 } else {
7370 CastTarget::Int
7371 }
7372 }
7373 "bigint" | "int8" => {
7374 if matches!(self.peek(), Token::LBracket)
7375 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7376 {
7377 self.advance();
7378 self.advance();
7379 CastTarget::BigIntArray
7380 } else {
7381 CastTarget::BigInt
7382 }
7383 }
7384 "float" | "double" | "real" => CastTarget::Float,
7385 "text" => {
7386 if matches!(self.peek(), Token::LBracket)
7388 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7389 {
7390 self.advance();
7391 self.advance();
7392 CastTarget::TextArray
7393 } else {
7394 CastTarget::Text
7395 }
7396 }
7397 "bool" | "boolean" => CastTarget::Bool,
7398 "vector" => CastTarget::Vector,
7399 "date" => CastTarget::Date,
7400 "timestamp" | "datetime" => CastTarget::Timestamp,
7401 "timestamptz" => CastTarget::Timestamptz,
7402 "interval" => CastTarget::Interval,
7403 "json" => CastTarget::Json,
7404 "jsonb" => CastTarget::Jsonb,
7405 "regtype" => CastTarget::RegType,
7406 "regclass" => CastTarget::RegClass,
7407 "tsvector" => CastTarget::TsVector,
7411 "tsquery" => CastTarget::TsQuery,
7412 "uuid" => CastTarget::Uuid,
7415 "bytea" => CastTarget::Bytea,
7420 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7425 other => {
7426 return Err(ParseError {
7427 message: format!("unsupported cast target `::{other}`"),
7428 token_pos: self.pos.saturating_sub(1),
7429 });
7430 }
7431 },
7432 Token::Interval => CastTarget::Interval,
7433 other => {
7434 return Err(ParseError {
7435 message: format!("expected type ident after `::`, got {other:?}"),
7436 token_pos: self.pos.saturating_sub(1),
7437 });
7438 }
7439 };
7440 expr = Expr::Cast {
7441 expr: Box::new(expr),
7442 target,
7443 };
7444 continue;
7445 }
7446 if matches!(self.peek(), Token::Is) {
7447 self.advance();
7448 let negated = if matches!(self.peek(), Token::Not) {
7449 self.advance();
7450 true
7451 } else {
7452 false
7453 };
7454 if matches!(self.peek(), Token::Distinct) {
7457 self.advance();
7458 if !matches!(self.peek(), Token::From) {
7459 return Err(self.err(format!(
7460 "expected FROM after IS{} DISTINCT, got {:?}",
7461 if negated { " NOT" } else { "" },
7462 self.peek()
7463 )));
7464 }
7465 self.advance();
7466 let rhs = self.parse_expr(20)?;
7470 let op = if negated {
7471 BinOp::IsNotDistinctFrom
7472 } else {
7473 BinOp::IsDistinctFrom
7474 };
7475 expr = Expr::Binary {
7476 op,
7477 lhs: Box::new(expr),
7478 rhs: Box::new(rhs),
7479 };
7480 continue;
7481 }
7482 if !matches!(self.peek(), Token::Null) {
7483 return Err(self.err(format!(
7484 "expected NULL or DISTINCT after IS{}, got {:?}",
7485 if negated { " NOT" } else { "" },
7486 self.peek()
7487 )));
7488 }
7489 self.advance();
7490 expr = Expr::IsNull {
7491 expr: Box::new(expr),
7492 negated,
7493 };
7494 continue;
7495 }
7496 let negated = if matches!(self.peek(), Token::Not) {
7500 let next = self.tokens.get(self.pos + 1);
7501 matches!(next, Some(Token::Between | Token::In | Token::Like))
7502 } else {
7503 false
7504 };
7505 if negated {
7506 self.advance();
7507 }
7508 if matches!(self.peek(), Token::Between) {
7509 expr = self.parse_between_tail(expr, negated)?;
7510 continue;
7511 }
7512 if matches!(self.peek(), Token::In) {
7513 expr = self.parse_in_tail(expr, negated)?;
7514 continue;
7515 }
7516 if matches!(self.peek(), Token::Like) {
7517 self.advance();
7518 let pattern = self.parse_expr(5)?;
7521 expr = Expr::Like {
7522 expr: Box::new(expr),
7523 pattern: Box::new(pattern),
7524 negated,
7525 };
7526 continue;
7527 }
7528 if matches!(self.peek(), Token::LBracket) {
7532 self.advance();
7533 let index = self.parse_expr(0)?;
7534 if !matches!(self.peek(), Token::RBracket) {
7535 return Err(self.err(alloc::format!(
7536 "expected ']' after array index, got {:?}",
7537 self.peek()
7538 )));
7539 }
7540 self.advance();
7541 expr = Expr::ArraySubscript {
7542 target: Box::new(expr),
7543 index: Box::new(index),
7544 };
7545 continue;
7546 }
7547 return Ok(expr);
7548 }
7549 }
7550
7551 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7555 self.advance(); let low = self.parse_expr(5)?;
7557 if !matches!(self.peek(), Token::And) {
7558 return Err(self.err(format!(
7559 "expected AND after BETWEEN low bound, got {:?}",
7560 self.peek()
7561 )));
7562 }
7563 self.advance();
7564 let high = self.parse_expr(5)?;
7565 let target = Box::new(expr);
7566 let combined = Expr::Binary {
7567 lhs: Box::new(Expr::Binary {
7568 lhs: target.clone(),
7569 op: BinOp::GtEq,
7570 rhs: Box::new(low),
7571 }),
7572 op: BinOp::And,
7573 rhs: Box::new(Expr::Binary {
7574 lhs: target,
7575 op: BinOp::LtEq,
7576 rhs: Box::new(high),
7577 }),
7578 };
7579 Ok(maybe_not(combined, negated))
7580 }
7581
7582 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
7587 let mut recursive = false;
7592 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
7593 && s.eq_ignore_ascii_case("recursive")
7594 {
7595 self.advance();
7596 recursive = true;
7597 }
7598 let mut ctes = Vec::new();
7599 loop {
7600 let name = self.expect_ident_like()?;
7601 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
7605 self.advance();
7606 let mut names = Vec::new();
7607 loop {
7608 names.push(self.expect_ident_like()?);
7609 if matches!(self.peek(), Token::Comma) {
7610 self.advance();
7611 continue;
7612 }
7613 break;
7614 }
7615 if !matches!(self.peek(), Token::RParen) {
7616 return Err(self.err(format!(
7617 "expected ')' to close CTE column list, got {:?}",
7618 self.peek()
7619 )));
7620 }
7621 self.advance();
7622 names
7623 } else {
7624 Vec::new()
7625 };
7626 if !matches!(self.peek(), Token::As) {
7630 return Err(self.err(format!(
7631 "expected AS after CTE name {name:?}, got {:?}",
7632 self.peek()
7633 )));
7634 }
7635 self.advance();
7636 if !matches!(self.peek(), Token::LParen) {
7637 return Err(self.err(format!(
7638 "expected '(' after AS in WITH clause, got {:?}",
7639 self.peek()
7640 )));
7641 }
7642 self.advance();
7643 if !matches!(self.peek(), Token::Select) {
7644 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
7645 }
7646 let inner = self.parse_select_stmt()?;
7647 if !matches!(self.peek(), Token::RParen) {
7648 return Err(self.err(format!(
7649 "expected ')' after CTE body, got {:?}",
7650 self.peek()
7651 )));
7652 }
7653 self.advance();
7654 let Statement::Select(body) = inner else {
7655 unreachable!("parse_select_stmt returns Select")
7656 };
7657 ctes.push(crate::ast::Cte {
7658 name,
7659 body,
7660 recursive,
7661 column_overrides,
7662 });
7663 if matches!(self.peek(), Token::Comma) {
7664 self.advance();
7665 continue;
7666 }
7667 break;
7668 }
7669 if !matches!(self.peek(), Token::Select) {
7671 return Err(self.err(format!(
7672 "expected SELECT after WITH clause, got {:?}",
7673 self.peek()
7674 )));
7675 }
7676 let body_stmt = self.parse_select_stmt()?;
7677 let Statement::Select(mut body) = body_stmt else {
7678 unreachable!()
7679 };
7680 body.ctes = ctes;
7681 Ok(Statement::Select(body))
7682 }
7683
7684 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
7693 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
7697 None
7698 } else {
7699 Some(Box::new(self.parse_expr(0)?))
7700 };
7701 let mut branches: Vec<(Expr, Expr)> = Vec::new();
7702 loop {
7703 match self.peek() {
7704 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
7705 self.advance();
7706 let cond = self.parse_expr(0)?;
7707 match self.peek() {
7708 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
7709 self.advance();
7710 }
7711 other => {
7712 return Err(self.err(alloc::format!(
7713 "expected THEN after CASE WHEN <expr>, got {other:?}"
7714 )));
7715 }
7716 }
7717 let value = self.parse_expr(0)?;
7718 branches.push((cond, value));
7719 }
7720 _ => break,
7721 }
7722 }
7723 if branches.is_empty() {
7724 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
7725 }
7726 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
7727 {
7728 self.advance();
7729 Some(Box::new(self.parse_expr(0)?))
7730 } else {
7731 None
7732 };
7733 match self.peek() {
7734 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
7735 self.advance();
7736 }
7737 other => {
7738 return Err(self.err(alloc::format!(
7739 "expected END to close CASE expression, got {other:?}"
7740 )));
7741 }
7742 }
7743 Ok(Expr::Case {
7744 operand,
7745 branches,
7746 else_branch,
7747 })
7748 }
7749
7750 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
7751 if !matches!(self.peek(), Token::LParen) {
7752 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
7753 }
7754 self.advance();
7755 let inner = self.parse_select_stmt()?;
7756 if !matches!(self.peek(), Token::RParen) {
7757 return Err(self.err(format!(
7758 "expected ')' after EXISTS-subquery, got {:?}",
7759 self.peek()
7760 )));
7761 }
7762 self.advance();
7763 let Statement::Select(s) = inner else {
7764 unreachable!("parse_select_stmt returns Select")
7765 };
7766 Ok(Expr::Exists {
7767 subquery: Box::new(s),
7768 negated,
7769 })
7770 }
7771
7772 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7773 self.advance(); if !matches!(self.peek(), Token::LParen) {
7775 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
7776 }
7777 self.advance();
7778 if matches!(self.peek(), Token::Select) {
7780 let inner = self.parse_select_stmt()?;
7781 if !matches!(self.peek(), Token::RParen) {
7782 return Err(self.err(format!(
7783 "expected ')' after IN-subquery, got {:?}",
7784 self.peek()
7785 )));
7786 }
7787 self.advance();
7788 let Statement::Select(s) = inner else {
7789 unreachable!("parse_select_stmt always returns Statement::Select")
7790 };
7791 return Ok(Expr::InSubquery {
7792 expr: Box::new(expr),
7793 subquery: Box::new(s),
7794 negated,
7795 });
7796 }
7797 let mut elements = Vec::new();
7798 if !matches!(self.peek(), Token::RParen) {
7799 loop {
7800 elements.push(self.parse_expr(0)?);
7801 match self.peek() {
7802 Token::Comma => {
7803 self.advance();
7804 }
7805 Token::RParen => break,
7806 other => {
7807 return Err(
7808 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
7809 );
7810 }
7811 }
7812 }
7813 }
7814 self.advance(); let target = Box::new(expr);
7816 let combined = if elements.is_empty() {
7817 Expr::Literal(Literal::Bool(false))
7818 } else {
7819 let mut iter = elements.into_iter();
7820 let first = iter.next().unwrap();
7821 let mut acc = Expr::Binary {
7822 lhs: target.clone(),
7823 op: BinOp::Eq,
7824 rhs: Box::new(first),
7825 };
7826 for elt in iter {
7827 acc = Expr::Binary {
7828 lhs: Box::new(acc),
7829 op: BinOp::Or,
7830 rhs: Box::new(Expr::Binary {
7831 lhs: target.clone(),
7832 op: BinOp::Eq,
7833 rhs: Box::new(elt),
7834 }),
7835 };
7836 }
7837 acc
7838 };
7839 Ok(maybe_not(combined, negated))
7840 }
7841
7842 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
7863 if !matches!(self.peek(), Token::LParen) {
7866 return Err(self.err(alloc::format!(
7867 "expected '(' after MATCH, got {:?}",
7868 self.peek()
7869 )));
7870 }
7871 self.advance();
7872 let mut cols: Vec<Expr> = Vec::new();
7873 loop {
7874 cols.push(self.parse_expr(0)?);
7875 match self.peek() {
7876 Token::Comma => {
7877 self.advance();
7878 }
7879 Token::RParen => break,
7880 other => {
7881 return Err(self.err(alloc::format!(
7882 "expected ',' or ')' in MATCH column list, got {other:?}"
7883 )));
7884 }
7885 }
7886 }
7887 self.advance(); match self.peek() {
7890 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
7891 self.advance();
7892 }
7893 other => {
7894 return Err(self.err(alloc::format!(
7895 "expected AGAINST after MATCH column list, got {other:?}"
7896 )));
7897 }
7898 }
7899 if !matches!(self.peek(), Token::LParen) {
7900 return Err(self.err(alloc::format!(
7901 "expected '(' after AGAINST, got {:?}",
7902 self.peek()
7903 )));
7904 }
7905 self.advance();
7906 let term = match self.advance() {
7916 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
7917 Token::Placeholder(n) => Expr::Placeholder(n),
7918 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
7919 qualifier: None,
7920 name: s,
7921 }),
7922 other => {
7923 return Err(self.err(alloc::format!(
7924 "MATCH ... AGAINST(<term>) expects a string literal, \
7925 bound parameter, or column ref, got {other:?}"
7926 )));
7927 }
7928 };
7929 loop {
7934 match self.peek() {
7935 Token::In => {
7938 self.advance();
7939 }
7940 Token::Ident(s) | Token::QuotedIdent(s)
7941 if s.eq_ignore_ascii_case("natural")
7942 || s.eq_ignore_ascii_case("language")
7943 || s.eq_ignore_ascii_case("boolean")
7944 || s.eq_ignore_ascii_case("mode")
7945 || s.eq_ignore_ascii_case("with")
7946 || s.eq_ignore_ascii_case("query")
7947 || s.eq_ignore_ascii_case("expansion") =>
7948 {
7949 self.advance();
7950 }
7951 _ => break,
7952 }
7953 }
7954 if !matches!(self.peek(), Token::RParen) {
7955 return Err(self.err(alloc::format!(
7956 "expected ')' to close AGAINST, got {:?}",
7957 self.peek()
7958 )));
7959 }
7960 self.advance();
7961 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
7964 let plainto = Expr::FunctionCall {
7965 name: String::from("plainto_tsquery"),
7966 args: alloc::vec![simple_lit(), term.clone()],
7967 };
7968 let mut folded: Option<Expr> = None;
7969 for col in cols {
7970 let to_tsv = Expr::FunctionCall {
7971 name: String::from("to_tsvector"),
7972 args: alloc::vec![simple_lit(), col],
7973 };
7974 let leaf = Expr::Binary {
7975 lhs: Box::new(to_tsv),
7976 op: crate::ast::BinOp::TsMatch,
7977 rhs: Box::new(plainto.clone()),
7978 };
7979 folded = Some(match folded {
7980 None => leaf,
7981 Some(prev) => Expr::Binary {
7982 lhs: Box::new(prev),
7983 op: crate::ast::BinOp::Or,
7984 rhs: Box::new(leaf),
7985 },
7986 });
7987 }
7988 match folded {
7989 Some(e) => Ok(e),
7990 None => Err(self.err(String::from(
7991 "MATCH(...) AGAINST(...) requires at least one column",
7992 ))),
7993 }
7994 }
7995
7996 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
7997 if !matches!(self.peek(), Token::LParen) {
7998 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
7999 }
8000 self.advance();
8001 let field_name = self.expect_ident_like()?;
8002 let field = match field_name.to_ascii_lowercase().as_str() {
8003 "year" => ExtractField::Year,
8004 "month" => ExtractField::Month,
8005 "day" => ExtractField::Day,
8006 "hour" => ExtractField::Hour,
8007 "minute" => ExtractField::Minute,
8008 "second" => ExtractField::Second,
8009 "microsecond" | "microseconds" => ExtractField::Microsecond,
8010 other => {
8011 return Err(self.err(format!(
8012 "unknown EXTRACT field {other:?}; \
8013 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
8014 )));
8015 }
8016 };
8017 if !matches!(self.peek(), Token::From) {
8018 return Err(self.err(format!(
8019 "expected FROM after EXTRACT field, got {:?}",
8020 self.peek()
8021 )));
8022 }
8023 self.advance();
8024 let source = self.parse_expr(0)?;
8025 if !matches!(self.peek(), Token::RParen) {
8026 return Err(self.err(format!(
8027 "expected ')' to close EXTRACT, got {:?}",
8028 self.peek()
8029 )));
8030 }
8031 self.advance();
8032 Ok(Expr::Extract {
8033 field,
8034 source: Box::new(source),
8035 })
8036 }
8037
8038 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8043 let tok = self.advance();
8044 let Token::String(text) = tok else {
8045 return Err(self.err(format!(
8046 "expected string literal after INTERVAL, got {tok:?}"
8047 )));
8048 };
8049 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8050 message: format!(
8051 "cannot parse INTERVAL {text:?}; \
8052 expected `<n> <unit> [<n> <unit> ...]` with units \
8053 microsecond[s], millisecond[s], second[s], minute[s], \
8054 hour[s], day[s], week[s], month[s], year[s]"
8055 ),
8056 token_pos: self.pos.saturating_sub(1),
8057 })?;
8058 Ok(Expr::Literal(Literal::Interval {
8059 months,
8060 micros,
8061 text,
8062 }))
8063 }
8064
8065 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8066 let mut elems = Vec::new();
8067 if matches!(self.peek(), Token::RBracket) {
8068 self.advance();
8069 return Ok(Expr::Literal(Literal::Vector(elems)));
8070 }
8071 loop {
8072 let e = self.parse_expr(0)?;
8073 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8074 message: format!("vector element must be a numeric literal, got {e:?}"),
8075 token_pos: self.pos,
8076 })?;
8077 elems.push(x);
8078 match self.peek() {
8079 Token::Comma => {
8080 self.advance();
8081 }
8082 Token::RBracket => {
8083 self.advance();
8084 break;
8085 }
8086 other => {
8087 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8088 }
8089 }
8090 }
8091 Ok(Expr::Literal(Literal::Vector(elems)))
8092 }
8093
8094 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8103 let Token::Ident(s) = self.peek().clone() else {
8104 return NullTreatment::Respect;
8105 };
8106 let is_ignore = s.eq_ignore_ascii_case("ignore");
8107 let is_respect = s.eq_ignore_ascii_case("respect");
8108 if !is_ignore && !is_respect {
8109 return NullTreatment::Respect;
8110 }
8111 if self.pos + 1 < self.tokens.len()
8114 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8115 && s2.eq_ignore_ascii_case("nulls")
8116 {
8117 self.advance();
8118 self.advance();
8119 return if is_ignore {
8120 NullTreatment::Ignore
8121 } else {
8122 NullTreatment::Respect
8123 };
8124 }
8125 NullTreatment::Respect
8126 }
8127
8128 #[allow(clippy::type_complexity)] fn parse_over_clause(
8131 &mut self,
8132 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
8133 if !matches!(self.peek(), Token::LParen) {
8134 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8135 }
8136 self.advance();
8137 let mut partition_by = Vec::new();
8138 let mut order_by = Vec::new();
8139 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8141 && s.eq_ignore_ascii_case("partition")
8142 {
8143 self.advance();
8144 if !matches!(self.peek(), Token::By) {
8145 return Err(self.err(format!(
8146 "expected BY after PARTITION, got {:?}",
8147 self.peek()
8148 )));
8149 }
8150 self.advance();
8151 loop {
8152 partition_by.push(self.parse_expr(0)?);
8153 if matches!(self.peek(), Token::Comma) {
8154 self.advance();
8155 continue;
8156 }
8157 break;
8158 }
8159 }
8160 if matches!(self.peek(), Token::Order) {
8162 self.advance();
8163 if !matches!(self.peek(), Token::By) {
8164 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8165 }
8166 self.advance();
8167 loop {
8168 let e = self.parse_expr(0)?;
8169 let desc = if matches!(self.peek(), Token::Desc) {
8170 self.advance();
8171 true
8172 } else if matches!(self.peek(), Token::Asc) {
8173 self.advance();
8174 false
8175 } else {
8176 false
8177 };
8178 order_by.push((e, desc));
8179 if matches!(self.peek(), Token::Comma) {
8180 self.advance();
8181 continue;
8182 }
8183 break;
8184 }
8185 }
8186 let mut frame: Option<WindowFrame> = None;
8190 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8191 let kind = if s.eq_ignore_ascii_case("rows") {
8192 Some(FrameKind::Rows)
8193 } else if s.eq_ignore_ascii_case("range") {
8194 Some(FrameKind::Range)
8195 } else {
8196 None
8197 };
8198 if let Some(kind) = kind {
8199 self.advance();
8200 frame = Some(self.parse_frame_tail(kind)?);
8201 }
8202 }
8203 if !matches!(self.peek(), Token::RParen) {
8204 return Err(self.err(format!(
8205 "expected ')' to close OVER clause, got {:?}",
8206 self.peek()
8207 )));
8208 }
8209 self.advance();
8210 Ok((partition_by, order_by, frame))
8211 }
8212
8213 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8219 if matches!(self.peek(), Token::Between) {
8220 self.advance();
8221 let start = self.parse_frame_bound()?;
8222 if !matches!(self.peek(), Token::And) {
8223 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8224 }
8225 self.advance();
8226 let end = self.parse_frame_bound()?;
8227 Ok(WindowFrame {
8228 kind,
8229 start,
8230 end: Some(end),
8231 })
8232 } else {
8233 let start = self.parse_frame_bound()?;
8234 Ok(WindowFrame {
8235 kind,
8236 start,
8237 end: None,
8238 })
8239 }
8240 }
8241
8242 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8245 if let Token::Integer(n) = *self.peek() {
8247 self.advance();
8248 let n: u64 = u64::try_from(n).map_err(|_| {
8249 self.err(format!(
8250 "invalid frame offset {n} — expected non-negative integer"
8251 ))
8252 })?;
8253 let dir = self.expect_ident_like()?;
8254 return if dir.eq_ignore_ascii_case("preceding") {
8255 Ok(FrameBound::OffsetPreceding(n))
8256 } else if dir.eq_ignore_ascii_case("following") {
8257 Ok(FrameBound::OffsetFollowing(n))
8258 } else {
8259 Err(self.err(format!(
8260 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8261 )))
8262 };
8263 }
8264 let first = self.expect_ident_like()?;
8265 if first.eq_ignore_ascii_case("unbounded") {
8266 let dir = self.expect_ident_like()?;
8267 return if dir.eq_ignore_ascii_case("preceding") {
8268 Ok(FrameBound::UnboundedPreceding)
8269 } else if dir.eq_ignore_ascii_case("following") {
8270 Ok(FrameBound::UnboundedFollowing)
8271 } else {
8272 Err(self.err(format!(
8273 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8274 )))
8275 };
8276 }
8277 if first.eq_ignore_ascii_case("current") {
8278 let row = self.expect_ident_like()?;
8279 if !row.eq_ignore_ascii_case("row") {
8280 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8281 }
8282 return Ok(FrameBound::CurrentRow);
8283 }
8284 Err(self.err(format!(
8285 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8286 )))
8287 }
8288
8289 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8290 if matches!(self.peek(), Token::Dot) {
8291 self.advance();
8292 let name = self.expect_ident_like()?;
8293 if matches!(self.peek(), Token::LParen) {
8299 return self.finish_ident_atom(name);
8300 }
8301 return Ok(Expr::Column(ColumnName {
8302 qualifier: Some(first),
8303 name,
8304 }));
8305 }
8306 if matches!(self.peek(), Token::LParen) {
8307 self.advance();
8308 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8312 self.advance();
8313 if !matches!(self.peek(), Token::RParen) {
8314 return Err(self.err(format!(
8315 "expected ')' after COUNT(*), got {:?}",
8316 self.peek()
8317 )));
8318 }
8319 self.advance();
8320 let null_treatment = self.parse_null_treatment_modifier();
8322 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8323 && s.eq_ignore_ascii_case("over")
8324 {
8325 self.advance();
8326 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8327 return Ok(Expr::WindowFunction {
8328 name: "count_star".into(),
8329 args: Vec::new(),
8330 partition_by,
8331 order_by,
8332 frame,
8333 null_treatment,
8334 });
8335 }
8336 return Ok(Expr::FunctionCall {
8337 name: "count_star".into(),
8338 args: Vec::new(),
8339 });
8340 }
8341 let mut args = Vec::new();
8343 if !matches!(self.peek(), Token::RParen) {
8344 loop {
8345 args.push(self.parse_expr(0)?);
8346 match self.peek() {
8347 Token::Comma => {
8348 self.advance();
8349 }
8350 Token::RParen => break,
8351 other => {
8352 return Err(self.err(format!(
8353 "expected ',' or ')' in function args, got {other:?}"
8354 )));
8355 }
8356 }
8357 }
8358 }
8359 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8367 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8368 && s.eq_ignore_ascii_case("over")
8369 {
8370 self.advance();
8371 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8372 return Ok(Expr::WindowFunction {
8373 name: first,
8374 args,
8375 partition_by,
8376 order_by,
8377 frame,
8378 null_treatment,
8379 });
8380 }
8381 return Ok(Expr::FunctionCall { name: first, args });
8382 }
8383 let lc = first.to_ascii_lowercase();
8389 if matches!(
8390 lc.as_str(),
8391 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8392 ) {
8393 return Ok(Expr::FunctionCall {
8394 name: lc,
8395 args: Vec::new(),
8396 });
8397 }
8398 Ok(Expr::Column(ColumnName {
8399 qualifier: None,
8400 name: first,
8401 }))
8402 }
8403}
8404
8405fn extract_first_column(expr: &Expr) -> Option<String> {
8413 match expr {
8414 Expr::Column(cn) => Some(cn.name.clone()),
8415 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8416 Expr::Binary { lhs, rhs, .. } => {
8417 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8418 }
8419 Expr::Unary { expr: e, .. } => extract_first_column(e),
8420 _ => None,
8421 }
8422}
8423
8424fn maybe_not(expr: Expr, negated: bool) -> Expr {
8425 if negated {
8426 Expr::Unary {
8427 op: UnOp::Not,
8428 expr: Box::new(expr),
8429 }
8430 } else {
8431 expr
8432 }
8433}
8434
8435fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8436 let pair = match tok {
8437 Token::Or => (BinOp::Or, 1),
8438 Token::And => (BinOp::And, 2),
8439 Token::Eq => (BinOp::Eq, 4),
8440 Token::NotEq => (BinOp::NotEq, 4),
8441 Token::Lt => (BinOp::Lt, 4),
8442 Token::LtEq => (BinOp::LtEq, 4),
8443 Token::Gt => (BinOp::Gt, 4),
8444 Token::GtEq => (BinOp::GtEq, 4),
8445 Token::L2Distance => (BinOp::L2Distance, 5),
8448 Token::InnerProduct => (BinOp::InnerProduct, 5),
8449 Token::CosineDistance => (BinOp::CosineDistance, 5),
8450 Token::Plus => (BinOp::Add, 6),
8451 Token::Minus => (BinOp::Sub, 6),
8452 Token::Concat => (BinOp::Concat, 6),
8455 Token::Star => (BinOp::Mul, 7),
8456 Token::Slash => (BinOp::Div, 7),
8457 Token::JsonGet => (BinOp::JsonGet, 7),
8461 Token::JsonGetText => (BinOp::JsonGetText, 7),
8462 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
8463 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
8464 Token::JsonContains => (BinOp::JsonContains, 7),
8465 Token::TsMatch => (BinOp::TsMatch, 4),
8469 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
8473 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
8474 Token::InetContains => (BinOp::InetContains, 4),
8475 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
8476 Token::InetOverlap => (BinOp::InetOverlap, 4),
8477 _ => return None,
8478 };
8479 Some(pair)
8480}
8481
8482#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
8483fn is_alias_stopword(s: &str) -> bool {
8495 matches!(
8496 s.to_ascii_lowercase().as_str(),
8497 "with"
8498 | "on"
8499 | "where"
8500 | "having"
8501 | "group"
8502 | "order"
8503 | "limit"
8504 | "offset"
8505 | "union"
8506 | "except"
8507 | "intersect"
8508 | "returning"
8509 | "set"
8510 | "values"
8511 | "for"
8512 | "lateral"
8513 | "left"
8514 | "right"
8515 | "inner"
8516 | "outer"
8517 | "full"
8518 | "cross"
8519 | "join"
8520 | "natural"
8521 | "using"
8522 | "fetch"
8523 )
8524}
8525
8526fn extract_numeric_literal(e: &Expr) -> Option<f32> {
8527 match e {
8528 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
8529 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
8530 Expr::Unary {
8531 op: UnOp::Neg,
8532 expr,
8533 } => extract_numeric_literal(expr).map(|x| -x),
8534 _ => None,
8535 }
8536}
8537
8538pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
8546 let parts: Vec<&str> = s.split_whitespace().collect();
8547 if parts.is_empty() || !parts.len().is_multiple_of(2) {
8548 return None;
8549 }
8550 let mut months: i32 = 0;
8551 let mut micros: i64 = 0;
8552 let mut i = 0;
8553 while i < parts.len() {
8554 let n: i64 = parts[i].parse().ok()?;
8555 let unit = parts[i + 1].to_ascii_lowercase();
8556 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
8557 match unit_stripped {
8558 "microsecond" => micros = micros.checked_add(n)?,
8559 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
8560 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
8561 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
8562 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
8563 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
8564 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
8565 "month" => {
8566 let n32 = i32::try_from(n).ok()?;
8567 months = months.checked_add(n32)?;
8568 }
8569 "year" => {
8570 let n32 = i32::try_from(n).ok()?;
8571 months = months.checked_add(n32.checked_mul(12)?)?;
8572 }
8573 _ => return None,
8574 }
8575 i += 2;
8576 }
8577 Some((months, micros))
8578}
8579
8580fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
8591 Some(match ident.to_ascii_lowercase().as_str() {
8592 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
8593 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
8594 "bigint" => ColumnTypeName::BigInt,
8595 "float" | "double" | "real" => ColumnTypeName::Float,
8596 "text" => ColumnTypeName::Text,
8597 "bool" | "boolean" => ColumnTypeName::Bool,
8598 "date" => ColumnTypeName::Date,
8599 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
8600 "timestamptz" => ColumnTypeName::Timestamptz,
8601 "json" => ColumnTypeName::Json,
8602 "jsonb" => ColumnTypeName::Jsonb,
8603 "bytea" | "bytes" => ColumnTypeName::Bytes,
8604 "tsvector" => ColumnTypeName::TsVector,
8605 "tsquery" => ColumnTypeName::TsQuery,
8606 "uuid" => ColumnTypeName::Uuid,
8607 "time" => ColumnTypeName::Time,
8608 "year" => ColumnTypeName::Year,
8609 "timetz" => ColumnTypeName::TimeTz,
8610 "money" => ColumnTypeName::Money,
8611 _ => return None,
8612 })
8613}
8614
8615pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8642 parse_plpgsql_body(body)
8643}
8644
8645fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8646 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
8650 message: alloc::format!("plpgsql body lex error: {e}"),
8651 token_pos: 0,
8652 })?;
8653 let mut parser = Parser::new(tokens);
8654 parser.parse_plpgsql_block()
8655}
8656
8657#[cfg(test)]
8658mod tests {
8659 use super::*;
8660 use alloc::string::ToString;
8661
8662 fn parse(s: &str) -> Statement {
8663 parse_statement(s).expect("parse ok")
8664 }
8665
8666 fn lit_int(n: i64) -> Expr {
8667 Expr::Literal(Literal::Integer(n))
8668 }
8669
8670 fn col(name: &str) -> Expr {
8671 Expr::Column(ColumnName {
8672 qualifier: None,
8673 name: name.into(),
8674 })
8675 }
8676
8677 #[test]
8678 fn select_single_integer() {
8679 let s = parse("SELECT 1");
8680 let Statement::Select(s) = s else {
8681 panic!("expected SELECT")
8682 };
8683 assert_eq!(s.items.len(), 1);
8684 assert!(s.from.is_none());
8685 assert!(s.where_.is_none());
8686 }
8687
8688 #[test]
8689 fn select_multiple_literal_kinds() {
8690 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
8691 let Statement::Select(s) = s else {
8692 panic!("expected SELECT")
8693 };
8694 assert_eq!(s.items.len(), 5);
8695 }
8696
8697 #[test]
8698 fn select_wildcard_from_table() {
8699 let s = parse("SELECT * FROM users");
8700 let Statement::Select(s) = s else {
8701 panic!("expected SELECT")
8702 };
8703 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
8704 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
8705 }
8706
8707 #[test]
8708 fn select_with_table_alias() {
8709 let s = parse("SELECT * FROM users AS u");
8710 let Statement::Select(s) = s else {
8711 panic!("expected SELECT")
8712 };
8713 let t = &s.from.as_ref().unwrap().primary;
8714 assert_eq!(t.name, "users");
8715 assert_eq!(t.alias.as_deref(), Some("u"));
8716 }
8717
8718 #[test]
8719 fn select_with_where_eq() {
8720 let s = parse("SELECT a FROM t WHERE a = 1");
8721 let Statement::Select(s) = s else {
8722 panic!("expected SELECT")
8723 };
8724 let w = s.where_.unwrap();
8725 assert_eq!(
8726 w,
8727 Expr::Binary {
8728 lhs: Box::new(col("a")),
8729 op: BinOp::Eq,
8730 rhs: Box::new(lit_int(1)),
8731 }
8732 );
8733 }
8734
8735 #[test]
8736 fn arithmetic_precedence() {
8737 let s = parse("SELECT 1 + 2 * 3");
8738 let Statement::Select(s) = s else {
8739 panic!("expected SELECT")
8740 };
8741 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8742 panic!("wildcard?")
8743 };
8744 assert_eq!(
8745 expr,
8746 &Expr::Binary {
8747 lhs: Box::new(lit_int(1)),
8748 op: BinOp::Add,
8749 rhs: Box::new(Expr::Binary {
8750 lhs: Box::new(lit_int(2)),
8751 op: BinOp::Mul,
8752 rhs: Box::new(lit_int(3)),
8753 }),
8754 }
8755 );
8756 }
8757
8758 #[test]
8759 fn parentheses_override_precedence() {
8760 let s = parse("SELECT (1 + 2) * 3");
8761 let Statement::Select(s) = s else {
8762 panic!("expected SELECT")
8763 };
8764 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8765 panic!()
8766 };
8767 assert_eq!(
8768 expr,
8769 &Expr::Binary {
8770 lhs: Box::new(Expr::Binary {
8771 lhs: Box::new(lit_int(1)),
8772 op: BinOp::Add,
8773 rhs: Box::new(lit_int(2)),
8774 }),
8775 op: BinOp::Mul,
8776 rhs: Box::new(lit_int(3)),
8777 }
8778 );
8779 }
8780
8781 #[test]
8782 fn not_binds_below_comparison() {
8783 let s = parse("SELECT NOT a = 1 FROM t");
8785 let Statement::Select(s) = s else {
8786 panic!("expected SELECT")
8787 };
8788 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8789 panic!()
8790 };
8791 assert_eq!(
8792 expr,
8793 &Expr::Unary {
8794 op: UnOp::Not,
8795 expr: Box::new(Expr::Binary {
8796 lhs: Box::new(col("a")),
8797 op: BinOp::Eq,
8798 rhs: Box::new(lit_int(1)),
8799 }),
8800 }
8801 );
8802 }
8803
8804 #[test]
8805 fn unary_minus_binds_above_multiplication() {
8806 let s = parse("SELECT -a * 2 FROM t");
8808 let Statement::Select(s) = s else {
8809 panic!("expected SELECT")
8810 };
8811 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8812 panic!()
8813 };
8814 assert_eq!(
8815 expr,
8816 &Expr::Binary {
8817 lhs: Box::new(Expr::Unary {
8818 op: UnOp::Neg,
8819 expr: Box::new(col("a")),
8820 }),
8821 op: BinOp::Mul,
8822 rhs: Box::new(lit_int(2)),
8823 }
8824 );
8825 }
8826
8827 #[test]
8828 fn qualified_column() {
8829 let s = parse("SELECT t.col FROM t");
8830 let Statement::Select(s) = s else {
8831 panic!("expected SELECT")
8832 };
8833 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8834 panic!()
8835 };
8836 assert_eq!(
8837 expr,
8838 &Expr::Column(ColumnName {
8839 qualifier: Some("t".into()),
8840 name: "col".into()
8841 })
8842 );
8843 }
8844
8845 #[test]
8846 fn select_item_alias_with_as() {
8847 let s = parse("SELECT a AS y FROM t");
8848 let Statement::Select(s) = s else {
8849 panic!("expected SELECT")
8850 };
8851 let SelectItem::Expr { alias, .. } = &s.items[0] else {
8852 panic!()
8853 };
8854 assert_eq!(alias.as_deref(), Some("y"));
8855 }
8856
8857 #[test]
8858 fn trailing_semicolon_accepted() {
8859 let s = parse("SELECT 1;");
8860 let Statement::Select(s) = s else {
8861 panic!("expected SELECT")
8862 };
8863 assert_eq!(s.items.len(), 1);
8864 }
8865
8866 #[test]
8867 fn boolean_chain_with_and_or_not() {
8868 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
8870 let Statement::Select(s) = s else {
8871 panic!("expected SELECT")
8872 };
8873 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8874 panic!()
8875 };
8876 let expected = Expr::Binary {
8877 lhs: Box::new(Expr::Unary {
8878 op: UnOp::Not,
8879 expr: Box::new(col("a")),
8880 }),
8881 op: BinOp::Or,
8882 rhs: Box::new(Expr::Binary {
8883 lhs: Box::new(col("b")),
8884 op: BinOp::And,
8885 rhs: Box::new(Expr::Unary {
8886 op: UnOp::Not,
8887 expr: Box::new(col("c")),
8888 }),
8889 }),
8890 };
8891 assert_eq!(expr, &expected);
8892 }
8893
8894 #[test]
8895 fn empty_input_errors() {
8896 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
8903 assert!(matches!(
8904 parse_statement(" \n\t ").unwrap(),
8905 Statement::Empty
8906 ));
8907 assert!(parse_statement("SELECT FROM WHERE").is_err());
8909 }
8910
8911 #[test]
8912 fn unmatched_paren_errors() {
8913 assert!(parse_statement("SELECT (1 + 2").is_err());
8914 }
8915
8916 #[test]
8917 fn display_round_trip_simple_select() {
8918 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
8919 let text = original.to_string();
8920 let again = parse_statement(&text).expect("re-parse");
8921 assert_eq!(original, again);
8922 }
8923
8924 #[test]
8927 fn create_table_single_column() {
8928 let s = parse("CREATE TABLE foo (a INT)");
8929 let Statement::CreateTable(c) = s else {
8930 panic!("expected CreateTable")
8931 };
8932 assert_eq!(c.name, "foo");
8933 assert_eq!(c.columns.len(), 1);
8934 assert_eq!(c.columns[0].name, "a");
8935 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
8936 assert!(c.columns[0].nullable);
8937 }
8938
8939 #[test]
8940 fn create_table_multi_column_with_not_null_mix() {
8941 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
8942 let Statement::CreateTable(c) = s else {
8943 panic!()
8944 };
8945 assert_eq!(c.columns.len(), 4);
8946 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
8947 assert!(!c.columns[0].nullable);
8948 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
8949 assert!(c.columns[1].nullable);
8950 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
8951 assert!(!c.columns[2].nullable);
8952 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
8953 }
8954
8955 #[test]
8956 fn create_table_bigint_supported() {
8957 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
8958 let Statement::CreateTable(c) = s else {
8959 panic!()
8960 };
8961 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
8962 }
8963
8964 #[test]
8965 fn create_table_vector_default_is_f32() {
8966 let s = parse("CREATE TABLE t (v VECTOR(128))");
8967 let Statement::CreateTable(c) = s else {
8968 panic!()
8969 };
8970 assert_eq!(
8971 c.columns[0].ty,
8972 ColumnTypeName::Vector {
8973 dim: 128,
8974 encoding: VecEncoding::F32,
8975 },
8976 );
8977 }
8978
8979 #[test]
8980 fn create_table_vector_using_sq8() {
8981 for sql in [
8984 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
8985 "CREATE TABLE t (v VECTOR(128) using sq8)",
8986 ] {
8987 let s = parse(sql);
8988 let Statement::CreateTable(c) = s else {
8989 panic!()
8990 };
8991 assert_eq!(
8992 c.columns[0].ty,
8993 ColumnTypeName::Vector {
8994 dim: 128,
8995 encoding: VecEncoding::Sq8,
8996 },
8997 "{sql}",
8998 );
8999 }
9000 }
9001
9002 #[test]
9003 fn create_table_vector_using_unknown_errors() {
9004 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
9013 assert!(
9014 err.message.contains("USING")
9015 || err.message.contains("using")
9016 || err.message.contains("')'")
9017 || err.message.contains("','"),
9018 "expected USING/column-list rejection, got: {}",
9019 err.message
9020 );
9021 }
9022
9023 #[test]
9024 fn vector_using_sq8_display_roundtrips() {
9025 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
9028 let Statement::CreateTable(c) = s else {
9029 panic!()
9030 };
9031 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9032 }
9033
9034 #[test]
9035 fn parser_recognises_placeholders() {
9036 use crate::ast::{Expr, SelectItem, Statement};
9037 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9039 let Statement::Select(sel) = s else { panic!() };
9040 assert!(matches!(
9041 sel.items[0],
9042 SelectItem::Expr {
9043 expr: Expr::Placeholder(1),
9044 alias: None
9045 }
9046 ));
9047 let SelectItem::Expr {
9049 expr: Expr::Binary { lhs, rhs, .. },
9050 ..
9051 } = &sel.items[1]
9052 else {
9053 panic!()
9054 };
9055 assert!(matches!(**lhs, Expr::Placeholder(2)));
9056 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9057 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9059 panic!()
9060 };
9061 assert!(matches!(**rhs, Expr::Placeholder(3)));
9062 }
9063
9064 #[test]
9065 fn parser_rejects_dollar_zero() {
9066 assert!(parse_statement("SELECT $0").is_err());
9068 }
9069
9070 #[test]
9071 fn placeholder_display_roundtrips() {
9072 let s = parse("SELECT $42 FROM t");
9075 let printed = s.to_string();
9076 assert!(printed.contains("$42"));
9077 let again = parse(&printed);
9078 assert_eq!(s, again);
9079 }
9080
9081 #[test]
9082 fn alter_index_rebuild_bare() {
9083 use crate::ast::{AlterIndexTarget, Statement};
9084 let s = parse("ALTER INDEX my_idx REBUILD");
9085 let Statement::AlterIndex(a) = s else {
9086 panic!("expected AlterIndex, got {s:?}")
9087 };
9088 assert_eq!(a.name, "my_idx");
9089 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9090 }
9091
9092 #[test]
9093 fn alter_index_rebuild_with_encoding() {
9094 use crate::ast::{AlterIndexTarget, Statement};
9095 for (sql, want) in [
9096 (
9097 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9098 VecEncoding::F32,
9099 ),
9100 (
9101 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9102 VecEncoding::Sq8,
9103 ),
9104 (
9105 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9106 VecEncoding::F16,
9107 ),
9108 ] {
9109 let s = parse(sql);
9110 let Statement::AlterIndex(a) = s else {
9111 panic!("{sql}: expected AlterIndex")
9112 };
9113 assert_eq!(a.name, "my_idx");
9114 assert_eq!(
9115 a.target,
9116 AlterIndexTarget::Rebuild {
9117 encoding: Some(want)
9118 },
9119 "{sql}"
9120 );
9121 }
9122 }
9123
9124 #[test]
9125 fn alter_index_rebuild_unknown_encoding_errors() {
9126 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9127 assert!(
9128 err.message.contains("unknown vector encoding"),
9129 "got: {}",
9130 err.message
9131 );
9132 }
9133
9134 #[test]
9135 fn alter_index_rebuild_display_roundtrips() {
9136 for (input, want) in [
9137 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9138 (
9139 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9140 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9141 ),
9142 (
9143 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9144 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9145 ),
9146 ] {
9147 let s = parse(input);
9148 assert_eq!(s.to_string(), want);
9149 }
9150 }
9151
9152 #[test]
9153 fn create_table_unknown_type_defers_to_engine() {
9154 let stmt = parse_statement("CREATE TABLE x (a xml)").unwrap();
9161 let Statement::CreateTable(t) = stmt else {
9162 panic!("expected CreateTable");
9163 };
9164 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("xml"));
9165 }
9166
9167 #[test]
9168 fn create_table_missing_table_keyword_errors() {
9169 assert!(parse_statement("CREATE x (a INT)").is_err());
9170 }
9171
9172 #[test]
9173 fn insert_single_value() {
9174 let s = parse("INSERT INTO foo VALUES (42)");
9175 let Statement::Insert(i) = s else {
9176 panic!("expected Insert")
9177 };
9178 assert_eq!(i.table, "foo");
9179 assert_eq!(i.rows.len(), 1);
9180 assert_eq!(i.rows[0].len(), 1);
9181 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9182 }
9183
9184 #[test]
9185 fn insert_multi_value_with_mixed_literals() {
9186 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9187 let Statement::Insert(i) = s else { panic!() };
9188 assert_eq!(i.rows.len(), 1);
9189 assert_eq!(i.rows[0].len(), 5);
9190 }
9191
9192 #[test]
9193 fn insert_missing_into_errors() {
9194 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9195 }
9196
9197 #[test]
9198 fn create_table_round_trip() {
9199 let original =
9200 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9201 let text = original.to_string();
9202 let again = parse_statement(&text).expect("re-parse");
9203 assert_eq!(original, again);
9204 }
9205
9206 #[test]
9207 fn insert_round_trip_with_negation_and_string() {
9208 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9209 let text = original.to_string();
9210 let again = parse_statement(&text).expect("re-parse");
9211 assert_eq!(original, again);
9212 }
9213
9214 #[test]
9215 fn unknown_keyword_at_statement_start_errors() {
9216 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9219 assert!(err.message.contains("expected SELECT"));
9220 }
9221
9222 #[test]
9225 fn create_index_basic() {
9226 let s = parse("CREATE INDEX idx_id ON users (id)");
9227 let Statement::CreateIndex(c) = s else {
9228 panic!("expected CreateIndex")
9229 };
9230 assert_eq!(c.name, "idx_id");
9231 assert_eq!(c.table, "users");
9232 assert_eq!(c.column, "id");
9233 }
9234
9235 #[test]
9236 fn create_index_missing_on_errors() {
9237 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9238 }
9239
9240 #[test]
9241 fn create_index_missing_paren_errors() {
9242 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9243 }
9244
9245 #[test]
9246 fn create_index_round_trip() {
9247 let original = parse("CREATE INDEX by_name ON users (name)");
9248 let again = parse_statement(&original.to_string()).unwrap();
9249 assert_eq!(original, again);
9250 }
9251
9252 #[test]
9255 fn create_unique_index_basic() {
9256 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9257 let Statement::CreateIndex(c) = s else {
9258 panic!("expected CreateIndex");
9259 };
9260 assert!(c.is_unique);
9261 assert_eq!(c.column, "a");
9262 assert!(c.partial_predicate.is_none());
9263 }
9264
9265 #[test]
9266 fn create_unique_index_partial() {
9267 let s = parse(
9269 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9270 ON email_templates (user_address) WHERE is_default = true",
9271 );
9272 let Statement::CreateIndex(c) = s else {
9273 panic!("expected CreateIndex");
9274 };
9275 assert!(c.is_unique);
9276 assert_eq!(c.table, "email_templates");
9277 assert_eq!(c.column, "user_address");
9278 assert!(c.partial_predicate.is_some());
9279 }
9280
9281 #[test]
9282 fn create_unique_index_composite_with_predicate() {
9283 let s = parse(
9285 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9286 ON calendar_events (calendar_id, uid, recurrence_id) \
9287 WHERE recurrence_id IS NOT NULL",
9288 );
9289 let Statement::CreateIndex(c) = s else {
9290 panic!("expected CreateIndex");
9291 };
9292 assert!(c.is_unique);
9293 assert_eq!(c.column, "calendar_id");
9294 assert_eq!(
9295 c.extra_columns,
9296 vec!["uid".to_string(), "recurrence_id".to_string()]
9297 );
9298 assert!(c.partial_predicate.is_some());
9299 }
9300
9301 #[test]
9302 fn create_unique_index_using_btree_ok() {
9303 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9304 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9305 }
9306
9307 #[test]
9308 fn create_unique_index_using_hnsw_rejected() {
9309 let err =
9310 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9311 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9312 }
9313
9314 #[test]
9315 fn create_unique_index_round_trip() {
9316 let original = parse(
9317 "CREATE UNIQUE INDEX uq_calendar_events_master \
9318 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9319 );
9320 let again = parse_statement(&original.to_string()).unwrap();
9321 assert_eq!(original, again);
9322 }
9323
9324 #[test]
9325 fn create_unique_without_index_errors() {
9326 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9327 assert!(err.message.contains("INDEX"), "{}", err.message);
9328 }
9329
9330 #[test]
9333 fn create_table_bytea_column() {
9334 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9335 let Statement::CreateTable(c) = s else {
9336 panic!("expected CreateTable");
9337 };
9338 assert_eq!(c.columns.len(), 2);
9339 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9340 assert!(!c.columns[1].nullable);
9341 }
9342
9343 #[test]
9344 fn create_table_bytes_alias_column() {
9345 let s = parse("CREATE TABLE t (blob BYTES)");
9346 let Statement::CreateTable(c) = s else {
9347 panic!("expected CreateTable");
9348 };
9349 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9350 }
9351
9352 #[test]
9353 fn bytea_round_trip_display() {
9354 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9355 let again = parse_statement(&original.to_string()).unwrap();
9356 assert_eq!(original, again);
9357 }
9358
9359 #[test]
9362 fn begin_commit_rollback_parse_as_unit_variants() {
9363 assert_eq!(parse("BEGIN"), Statement::Begin);
9364 assert_eq!(parse("COMMIT"), Statement::Commit);
9365 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9366 assert_eq!(parse("BEGIN;"), Statement::Begin);
9368 }
9369
9370 #[test]
9373 fn inner_product_binop_parses() {
9374 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9375 let Statement::Select(s) = s else { panic!() };
9376 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9377 panic!()
9378 };
9379 assert!(matches!(
9380 expr,
9381 Expr::Binary {
9382 op: BinOp::InnerProduct,
9383 ..
9384 }
9385 ));
9386 }
9387
9388 #[test]
9389 fn cosine_distance_binop_parses() {
9390 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9391 let Statement::Select(s) = s else { panic!() };
9392 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9393 panic!()
9394 };
9395 assert!(matches!(
9396 expr,
9397 Expr::Binary {
9398 op: BinOp::CosineDistance,
9399 ..
9400 }
9401 ));
9402 }
9403
9404 #[test]
9405 fn vector_cast_postfix_wraps_string_literal() {
9406 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9407 let Statement::Select(s) = s else { panic!() };
9408 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9409 panic!()
9410 };
9411 assert!(matches!(
9412 expr,
9413 Expr::Cast {
9414 target: CastTarget::Vector,
9415 ..
9416 }
9417 ));
9418 }
9419
9420 #[test]
9421 fn unsupported_cast_target_errors() {
9422 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
9424 assert!(err.message.contains("unsupported cast target"));
9425 }
9426
9427 #[test]
9428 fn tx_statements_round_trip() {
9429 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
9430 let original = parse(q);
9431 let again = parse_statement(&original.to_string()).unwrap();
9432 assert_eq!(original, again);
9433 }
9434 }
9435
9436 #[test]
9437 fn interval_text_parsing_units() {
9438 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
9440 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
9441 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
9442 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
9443 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
9445 assert_eq!(
9446 parse_interval_text("1 day 2 hours"),
9447 Some((0, 86_400_000_000 + 7_200_000_000))
9448 );
9449 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
9451 assert_eq!(parse_interval_text(""), None);
9453 assert_eq!(parse_interval_text("garbage"), None);
9454 assert_eq!(parse_interval_text("1 fortnight"), None);
9455 assert_eq!(parse_interval_text("1"), None);
9456 }
9457
9458 #[test]
9459 fn interval_literal_roundtrips_via_display() {
9460 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
9461 let s = parsed.to_string();
9462 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
9464 let again = parse_statement(&s).unwrap();
9466 assert_eq!(parsed, again);
9467 }
9468
9469 #[test]
9472 fn parser_recognises_create_publication_bare() {
9473 let s = parse("CREATE PUBLICATION pub_a");
9474 let Statement::CreatePublication(p) = s else {
9475 panic!("expected CreatePublication, got {s:?}")
9476 };
9477 assert_eq!(p.name, "pub_a");
9478 assert_eq!(p.scope, PublicationScope::AllTables);
9479 }
9480
9481 #[test]
9482 fn parser_recognises_create_publication_for_all_tables() {
9483 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
9484 let Statement::CreatePublication(p) = s else {
9485 panic!("expected CreatePublication, got {s:?}")
9486 };
9487 assert_eq!(p.name, "pub_a");
9488 assert_eq!(p.scope, PublicationScope::AllTables);
9489 }
9490
9491 #[test]
9492 fn parser_recognises_drop_publication() {
9493 let s = parse("DROP PUBLICATION pub_a");
9494 let Statement::DropPublication(name) = s else {
9495 panic!("expected DropPublication, got {s:?}")
9496 };
9497 assert_eq!(name, "pub_a");
9498 }
9499
9500 #[test]
9501 fn parser_recognises_for_table_list() {
9502 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
9503 let Statement::CreatePublication(p) = s else {
9504 panic!("expected CreatePublication, got {s:?}")
9505 };
9506 assert_eq!(p.name, "pub_a");
9507 let PublicationScope::ForTables(ts) = p.scope else {
9508 panic!("expected ForTables scope")
9509 };
9510 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
9511 }
9512
9513 #[test]
9514 fn parser_recognises_for_tables_plural() {
9515 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
9517 let Statement::CreatePublication(p) = s else {
9518 panic!("expected CreatePublication, got {s:?}")
9519 };
9520 let PublicationScope::ForTables(ts) = p.scope else {
9521 panic!("expected ForTables")
9522 };
9523 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9524 }
9525
9526 #[test]
9527 fn parser_recognises_for_all_tables_except_list() {
9528 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
9529 let Statement::CreatePublication(p) = s else {
9530 panic!()
9531 };
9532 let PublicationScope::AllTablesExcept(ts) = p.scope else {
9533 panic!("expected AllTablesExcept")
9534 };
9535 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9536 }
9537
9538 #[test]
9539 fn parser_rejects_for_table_with_empty_list() {
9540 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
9542 .expect_err("must error on empty list");
9543 assert!(!err.message.is_empty());
9546 }
9547
9548 #[test]
9549 fn parser_recognises_show_publications() {
9550 let s = parse("SHOW PUBLICATIONS");
9553 assert!(matches!(s, Statement::ShowPublications));
9554 }
9555
9556 #[test]
9559 fn parser_recognises_create_subscription_single_publication() {
9560 let s = parse(
9561 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
9562 );
9563 let Statement::CreateSubscription(c) = s else {
9564 panic!("expected CreateSubscription, got {s:?}")
9565 };
9566 assert_eq!(c.name, "sub_a");
9567 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
9568 assert_eq!(c.publications, alloc::vec!["pub_a"]);
9569 }
9570
9571 #[test]
9572 fn parser_recognises_create_subscription_multi_publication() {
9573 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
9574 let Statement::CreateSubscription(c) = s else {
9575 panic!()
9576 };
9577 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
9578 }
9579
9580 #[test]
9581 fn parser_rejects_create_subscription_missing_connection() {
9582 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
9583 .expect_err("must error on missing CONNECTION");
9584 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
9585 }
9586
9587 #[test]
9588 fn parser_rejects_create_subscription_missing_publication() {
9589 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
9590 .expect_err("must error on missing PUBLICATION");
9591 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
9592 }
9593
9594 #[test]
9595 fn parser_recognises_drop_subscription() {
9596 let s = parse("DROP SUBSCRIPTION sub_a");
9597 let Statement::DropSubscription(name) = s else {
9598 panic!("expected DropSubscription, got {s:?}")
9599 };
9600 assert_eq!(name, "sub_a");
9601 }
9602
9603 #[test]
9604 fn parser_recognises_show_subscriptions() {
9605 let s = parse("SHOW SUBSCRIPTIONS");
9606 assert!(matches!(s, Statement::ShowSubscriptions));
9607 }
9608
9609 #[test]
9610 fn parser_recognises_wait_for_wal_position_no_timeout() {
9611 let s = parse("WAIT FOR WAL POSITION 12345");
9612 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9613 panic!("expected WaitForWalPosition, got {s:?}")
9614 };
9615 assert_eq!(pos, 12345);
9616 assert!(timeout_ms.is_none());
9617 }
9618
9619 #[test]
9620 fn parser_recognises_wait_for_wal_position_with_timeout() {
9621 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
9622 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9623 panic!()
9624 };
9625 assert_eq!(pos, 67890);
9626 assert_eq!(timeout_ms, Some(5000));
9627 }
9628
9629 #[test]
9630 fn parser_rejects_wait_with_negative_position() {
9631 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
9637 assert!(!err.message.is_empty());
9638 }
9639
9640 #[test]
9641 fn parser_recognises_bare_analyze() {
9642 let s = parse("ANALYZE");
9643 assert!(matches!(s, Statement::Analyze(None)));
9644 }
9645
9646 #[test]
9647 fn parser_recognises_analyze_with_table() {
9648 let s = parse("ANALYZE users");
9649 let Statement::Analyze(Some(name)) = s else {
9650 panic!("expected Analyze, got {s:?}")
9651 };
9652 assert_eq!(name, "users");
9653 }
9654
9655 #[test]
9656 fn parser_recognises_analyze_with_quoted_table() {
9657 let s = parse("ANALYZE \"Mixed Case\"");
9658 let Statement::Analyze(Some(name)) = s else {
9659 panic!()
9660 };
9661 assert_eq!(name, "Mixed Case");
9662 }
9663
9664 #[test]
9665 fn parser_rejects_analyze_with_garbage_token() {
9666 let err = parse_statement("ANALYZE 42").expect_err("must error");
9667 assert!(!err.message.is_empty());
9668 }
9669
9670 #[test]
9671 fn analyze_display_roundtrips() {
9672 for sql in ["ANALYZE", "ANALYZE users"] {
9673 let s = parse(sql);
9674 let printed = s.to_string();
9675 let again = parse_statement(&printed)
9676 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9677 assert_eq!(s, again);
9678 }
9679 }
9680
9681 #[test]
9682 fn wait_for_display_roundtrips() {
9683 for sql in [
9684 "WAIT FOR WAL POSITION 12345",
9685 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
9686 ] {
9687 let s = parse(sql);
9688 let printed = s.to_string();
9689 let again = parse_statement(&printed)
9690 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9691 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9692 }
9693 }
9694
9695 #[test]
9696 fn subscription_ddl_display_roundtrips() {
9697 for sql in [
9698 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
9699 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
9700 "DROP SUBSCRIPTION sub_a",
9701 "SHOW SUBSCRIPTIONS",
9702 ] {
9703 let s = parse(sql);
9704 let printed = s.to_string();
9705 let again = parse_statement(&printed)
9706 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9707 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9708 }
9709 }
9710
9711 #[test]
9712 fn parser_drop_dispatches_user_vs_publication() {
9713 let s = parse("DROP USER 'alice'");
9716 let Statement::DropUser(name) = s else {
9717 panic!("expected DropUser, got {s:?}")
9718 };
9719 assert_eq!(name, "alice");
9720 let s = parse("DROP PUBLICATION p1");
9722 assert!(matches!(s, Statement::DropPublication(_)));
9723 }
9724
9725 #[test]
9726 fn publication_ddl_display_roundtrips() {
9727 for sql in [
9730 "CREATE PUBLICATION pub_a",
9731 "CREATE PUBLICATION pub_a FOR ALL TABLES",
9732 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
9733 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
9734 "DROP PUBLICATION pub_a",
9735 "SHOW PUBLICATIONS",
9736 ] {
9737 let s = parse(sql);
9738 let printed = s.to_string();
9739 let again = parse_statement(&printed)
9740 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9741 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9742 }
9743 }
9744
9745 #[test]
9748 fn create_function_returns_trigger_plpgsql_minimal() {
9749 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
9750 let s = parse(sql);
9751 let Statement::CreateFunction(f) = s else {
9752 panic!("expected CreateFunction");
9753 };
9754 assert_eq!(f.name, "noop");
9755 assert!(!f.or_replace);
9756 assert!(f.args.is_empty());
9757 assert!(matches!(f.returns, FunctionReturn::Trigger));
9758 assert_eq!(f.language, "plpgsql");
9759 let FunctionBody::PlPgSql(block) = f.body else {
9760 panic!("expected PlPgSql body");
9761 };
9762 assert_eq!(block.statements.len(), 1);
9763 assert!(matches!(
9764 block.statements[0],
9765 PlPgSqlStmt::Return(ReturnTarget::New)
9766 ));
9767 }
9768
9769 #[test]
9770 fn create_function_or_replace_with_assignment() {
9771 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
9774BEGIN
9775 NEW.search_vector := to_tsvector('english', NEW.subject);
9776 RETURN NEW;
9777END;
9778$$";
9779 let s = parse(sql);
9780 let Statement::CreateFunction(f) = s else {
9781 panic!("expected CreateFunction");
9782 };
9783 assert!(f.or_replace);
9784 let FunctionBody::PlPgSql(block) = &f.body else {
9785 panic!("expected PlPgSql body");
9786 };
9787 assert_eq!(block.statements.len(), 2);
9788 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
9790 panic!("expected Assign as first stmt");
9791 };
9792 match target {
9793 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
9794 other => panic!("expected NEW.col, got {other:?}"),
9795 }
9796 assert!(matches!(
9798 block.statements[1],
9799 PlPgSqlStmt::Return(ReturnTarget::New)
9800 ));
9801 }
9802
9803 #[test]
9804 fn create_trigger_after_insert_or_update() {
9805 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
9806 let s = parse(sql);
9807 let Statement::CreateTrigger(t) = s else {
9808 panic!("expected CreateTrigger");
9809 };
9810 assert_eq!(t.name, "tg");
9811 assert_eq!(t.table, "messages");
9812 assert_eq!(t.timing, TriggerTiming::After);
9813 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
9814 assert_eq!(t.for_each, TriggerForEach::Row);
9815 assert_eq!(t.function, "update_sv");
9816 }
9817
9818 #[test]
9819 fn create_trigger_before_delete_execute_procedure_alias() {
9820 let sql =
9822 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
9823 let s = parse(sql);
9824 let Statement::CreateTrigger(t) = s else {
9825 panic!("expected CreateTrigger");
9826 };
9827 assert_eq!(t.timing, TriggerTiming::Before);
9828 assert_eq!(t.events, vec![TriggerEvent::Delete]);
9829 }
9830
9831 #[test]
9832 fn drop_trigger_if_exists_round_trips() {
9833 let s = Statement::DropTrigger {
9838 name: "tg".into(),
9839 table: "messages".into(),
9840 if_exists: true,
9841 };
9842 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
9843 }
9844
9845 #[test]
9846 fn trigger_ddl_display_roundtrips_through_parser() {
9847 for sql in [
9851 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
9852 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
9853 ] {
9854 let s = parse(sql);
9855 let printed = s.to_string();
9856 let again = parse_statement(&printed)
9857 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9858 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9859 }
9860 }
9861}