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 try_peek_meta_bare(&mut self) -> Option<String> {
4181 const PG_META_TABLES: &[&str] = &[
4182 "pg_attribute",
4183 "pg_class",
4184 "pg_constraint",
4185 "pg_database",
4186 "pg_extension",
4187 "pg_index",
4188 "pg_indexes",
4189 "pg_matviews",
4190 "pg_namespace",
4191 "pg_proc",
4192 "pg_roles",
4193 "pg_settings",
4194 "pg_type",
4195 "pg_user",
4196 "pg_views",
4197 ];
4198 let name = match self.tokens.get(self.pos) {
4199 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4200 _ => return None,
4201 };
4202 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4205 return None;
4206 }
4207 if !PG_META_TABLES.contains(&name.as_str()) {
4208 return None;
4209 }
4210 self.advance();
4211 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4212 Some(alloc::format!("__spg_pg_{bare}"))
4213 }
4214
4215 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4217 match self.advance() {
4218 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4219 other => Err(ParseError {
4220 message: format!("expected {kw:?}, got {other:?}"),
4221 token_pos: self.pos.saturating_sub(1),
4222 }),
4223 }
4224 }
4225
4226 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4230 match self.advance() {
4231 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4232 other => Err(ParseError {
4233 message: format!("expected identifier or string, got {other:?}"),
4234 token_pos: self.pos.saturating_sub(1),
4235 }),
4236 }
4237 }
4238
4239 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4240 match self.advance() {
4241 Token::String(s) => Ok(s),
4242 other => Err(ParseError {
4243 message: format!("expected quoted string, got {other:?}"),
4244 token_pos: self.pos.saturating_sub(1),
4245 }),
4246 }
4247 }
4248
4249 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4250 let mut head = self.parse_bare_select()?;
4255 while matches!(self.peek(), Token::Union) {
4256 self.advance();
4257 let kind = if matches!(self.peek(), Token::All) {
4258 self.advance();
4259 UnionKind::All
4260 } else {
4261 UnionKind::Distinct
4262 };
4263 let peer = self.parse_bare_select()?;
4264 head.unions.push((kind, peer));
4265 }
4266 head.order_by = if matches!(self.peek(), Token::Order) {
4267 self.advance();
4268 if !matches!(self.peek(), Token::By) {
4269 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4270 }
4271 self.advance();
4272 let mut keys = Vec::new();
4275 loop {
4276 let expr = self.parse_expr(0)?;
4277 let desc = if matches!(self.peek(), Token::Desc) {
4278 self.advance();
4279 true
4280 } else if matches!(self.peek(), Token::Asc) {
4281 self.advance();
4282 false
4283 } else {
4284 false
4285 };
4286 keys.push(OrderBy { expr, desc });
4287 if matches!(self.peek(), Token::Comma) {
4288 self.advance();
4289 } else {
4290 break;
4291 }
4292 }
4293 keys
4294 } else {
4295 Vec::new()
4296 };
4297 head.limit = if matches!(self.peek(), Token::Limit) {
4298 self.advance();
4299 if self.consume_limit_unbounded_sentinel() {
4306 None
4307 } else {
4308 Some(self.parse_limit_expr("LIMIT")?)
4309 }
4310 } else {
4311 None
4312 };
4313 head.offset = if matches!(self.peek(), Token::Offset) {
4314 self.advance();
4315 let off = self.parse_limit_expr("OFFSET")?;
4319 self.consume_optional_rows_keyword();
4320 Some(off)
4321 } else {
4322 None
4323 };
4324 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4330 {
4331 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4334 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4335 {
4336 self.advance();
4337 }
4338 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4341 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4342 {
4343 crate::ast::LimitExpr::Literal(1)
4345 } else {
4346 self.parse_limit_expr("FETCH FIRST")?
4347 };
4348 self.consume_optional_rows_keyword();
4350 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4356 if s.eq_ignore_ascii_case("only"))
4357 {
4358 self.advance();
4359 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4360 if s.eq_ignore_ascii_case("with"))
4361 {
4362 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4364 if s.eq_ignore_ascii_case("ties"))
4365 {
4366 self.advance();
4367 head.limit_with_ties = true;
4368 }
4369 }
4370 head.limit = Some(count);
4371 }
4372 self.consume_optional_for_lock_clauses();
4387 Ok(Statement::Select(head))
4388 }
4389
4390 fn consume_optional_for_lock_clauses(&mut self) {
4397 while matches!(self.peek(), Token::For) {
4398 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4402 if s.eq_ignore_ascii_case("no"))
4403 {
4404 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4408 if s.eq_ignore_ascii_case("key"))
4409 {
4410 self.advance(); }
4412 }
4413 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4415 if s.eq_ignore_ascii_case("key"))
4416 {
4417 self.advance(); }
4419 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4425 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4426 {
4427 self.advance();
4428 } else {
4429 return;
4434 }
4435 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4438 if s.eq_ignore_ascii_case("of"))
4439 {
4440 self.advance(); #[allow(clippy::while_let_loop)]
4442 loop {
4443 match self.peek() {
4444 Token::Ident(_) | Token::QuotedIdent(_) => {
4445 self.advance();
4446 if matches!(self.peek(), Token::Dot) {
4448 self.advance();
4449 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4450 self.advance();
4451 }
4452 }
4453 }
4454 _ => break,
4455 }
4456 if matches!(self.peek(), Token::Comma) {
4457 self.advance();
4458 } else {
4459 break;
4460 }
4461 }
4462 }
4463 match self.peek().clone() {
4465 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4466 self.advance();
4467 }
4468 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4469 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4471 if s.eq_ignore_ascii_case("locked"))
4472 {
4473 self.advance(); }
4475 }
4476 _ => {}
4477 }
4478 }
4480 }
4481
4482 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4491 if matches!(self.peek(), Token::Null) {
4492 self.advance();
4493 return true;
4494 }
4495 if matches!(self.peek(), Token::All) {
4496 self.advance();
4497 return true;
4498 }
4499 false
4500 }
4501
4502 fn consume_optional_rows_keyword(&mut self) {
4506 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4507 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4508 {
4509 self.advance();
4510 }
4511 }
4512
4513 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4514 match self.advance() {
4515 Token::Integer(n) if n >= 0 => u32::try_from(n)
4516 .map(crate::ast::LimitExpr::Literal)
4517 .map_err(|_| ParseError {
4518 message: alloc::format!("{label} value too large: {n}"),
4519 token_pos: self.pos.saturating_sub(1),
4520 }),
4521 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4522 other => Err(ParseError {
4523 message: alloc::format!(
4524 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4525 ),
4526 token_pos: self.pos.saturating_sub(1),
4527 }),
4528 }
4529 }
4530
4531 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4536 if !matches!(self.peek(), Token::Select) {
4537 return Err(self.err(format!(
4538 "expected SELECT to start a query block, got {:?}",
4539 self.peek()
4540 )));
4541 }
4542 self.advance();
4543 let distinct = if matches!(self.peek(), Token::Distinct) {
4544 self.advance();
4545 true
4546 } else {
4547 false
4548 };
4549 let items = self.parse_select_list()?;
4550 let from = if matches!(self.peek(), Token::From) {
4551 self.advance();
4552 Some(self.parse_from_clause()?)
4553 } else {
4554 None
4555 };
4556 let where_ = if matches!(self.peek(), Token::Where) {
4557 self.advance();
4558 Some(self.parse_expr(0)?)
4559 } else {
4560 None
4561 };
4562 let mut group_by_all = false;
4563 let group_by = if matches!(self.peek(), Token::Group) {
4564 self.advance();
4565 if !matches!(self.peek(), Token::By) {
4566 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4567 }
4568 self.advance();
4569 if matches!(self.peek(), Token::All) {
4572 self.advance();
4573 group_by_all = true;
4574 None
4575 } else {
4576 let mut groups = Vec::new();
4577 loop {
4578 groups.push(self.parse_expr(0)?);
4579 if matches!(self.peek(), Token::Comma) {
4580 self.advance();
4581 } else {
4582 break;
4583 }
4584 }
4585 Some(groups)
4586 }
4587 } else {
4588 None
4589 };
4590 let having = if matches!(self.peek(), Token::Having) {
4591 self.advance();
4592 Some(self.parse_expr(0)?)
4593 } else {
4594 None
4595 };
4596 Ok(SelectStatement {
4597 ctes: Vec::new(),
4598 distinct,
4599 items,
4600 from,
4601 where_,
4602 group_by,
4603 group_by_all,
4604 having,
4605 unions: Vec::new(),
4606 order_by: Vec::new(),
4607 limit: None,
4608 offset: None,
4609 limit_with_ties: false,
4610 })
4611 }
4612
4613 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4614 debug_assert!(matches!(self.peek(), Token::Table));
4616 self.advance();
4617 let if_not_exists = self.consume_if_not_exists();
4618 let name = self.expect_ident_like()?;
4619 if !matches!(self.peek(), Token::LParen) {
4620 return Err(self.err(format!(
4621 "expected '(' after table name, got {:?}",
4622 self.peek()
4623 )));
4624 }
4625 self.advance();
4626 let mut columns = Vec::new();
4627 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4628 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4629 loop {
4630 if self.peek_table_level_pk_start() {
4636 table_constraints.push(self.parse_table_level_primary_key()?);
4637 } else if self.peek_table_level_unique_start() {
4638 table_constraints.push(self.parse_table_level_unique()?);
4639 } else if self.peek_table_level_check_start() {
4640 table_constraints.push(self.parse_table_level_check()?);
4642 } else if self.peek_mysql_inline_key_start() {
4643 if let Some(uc) = self.parse_mysql_inline_key()? {
4649 table_constraints.push(uc);
4650 }
4651 } else if self.peek_constraint_or_fk_start() {
4652 foreign_keys.push(self.parse_table_level_fk()?);
4653 } else {
4654 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
4655 if col.is_unique {
4659 table_constraints.push(crate::ast::TableConstraint::Unique {
4660 name: None,
4661 columns: alloc::vec![col.name.clone()],
4662 nulls_not_distinct: false,
4663 });
4664 }
4665 if let Some(check_expr) = col.check.clone() {
4666 table_constraints.push(crate::ast::TableConstraint::Check {
4667 name: None,
4668 expr: check_expr,
4669 });
4670 }
4671 columns.push(col);
4672 if let Some(fk) = col_level_fk {
4673 foreign_keys.push(fk);
4674 }
4675 }
4676 match self.peek() {
4677 Token::Comma => {
4678 self.advance();
4679 }
4680 Token::RParen => {
4681 self.advance();
4682 break;
4683 }
4684 other => {
4685 return Err(
4686 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
4687 );
4688 }
4689 }
4690 }
4691 if columns.is_empty() {
4692 return Err(self.err("CREATE TABLE requires at least one column".into()));
4693 }
4694 self.consume_mysql_table_options();
4701 Ok(Statement::CreateTable(CreateTableStatement {
4702 name,
4703 columns,
4704 if_not_exists,
4705 foreign_keys,
4706 table_constraints,
4707 }))
4708 }
4709
4710 fn peek_mysql_inline_key_start(&self) -> bool {
4719 let cur = self.peek();
4720 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
4730 match self.tokens.get(skip) {
4733 Some(Token::LParen) => true,
4734 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
4735 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
4736 }
4737 _ => false,
4738 }
4739 };
4740 let is_key_or_index_tok = |t: &Token| -> bool {
4744 matches!(t, Token::Index)
4745 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
4746 };
4747 match cur {
4748 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
4749 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4750 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
4751 }
4752 Token::Ident(s)
4753 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
4754 {
4755 let nxt = self.tokens.get(self.pos + 1);
4756 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
4757 self.pos + 2
4758 } else {
4759 self.pos + 1
4760 };
4761 after_keyword_followed_by_paren_or_ident_paren(after_after)
4762 }
4763 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4764 let nxt = self.tokens.get(self.pos + 1);
4765 if !nxt.is_some_and(is_key_or_index_tok) {
4766 return false;
4767 }
4768 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
4769 }
4770 _ => false,
4771 }
4772 }
4773
4774 fn parse_mysql_inline_key(
4783 &mut self,
4784 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
4785 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
4787 {
4788 self.advance();
4789 true
4790 } else {
4791 false
4792 };
4793 let mut is_fulltext = false;
4799 let mut is_spatial = false;
4800 if let Token::Ident(s) = self.peek().clone() {
4801 if s.eq_ignore_ascii_case("fulltext") {
4802 self.advance();
4803 is_fulltext = true;
4804 } else if s.eq_ignore_ascii_case("spatial") {
4805 self.advance();
4806 is_spatial = true;
4807 }
4808 }
4809 match self.peek() {
4812 Token::Index => {
4813 self.advance();
4814 }
4815 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4816 self.advance();
4817 }
4818 other => {
4819 return Err(self.err(alloc::format!(
4820 "expected KEY/INDEX in inline index declaration, got {other:?}"
4821 )));
4822 }
4823 }
4824 let mut idx_name: Option<String> = None;
4829 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
4830 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4831 {
4832 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
4833 idx_name = Some(s);
4834 }
4835 }
4836 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4838 self.advance();
4839 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4840 self.advance();
4841 }
4842 }
4843 if !matches!(self.peek(), Token::LParen) {
4845 return Err(self.err(alloc::format!(
4846 "expected '(' in inline KEY/INDEX, got {:?}",
4847 self.peek()
4848 )));
4849 }
4850 self.advance();
4851 let mut cols: Vec<String> = Vec::new();
4852 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
4853 self.advance();
4854 cols.push(s);
4855 if matches!(self.peek(), Token::LParen) {
4857 let mut depth = 1usize;
4858 self.advance();
4859 while depth > 0 {
4860 match self.peek() {
4861 Token::LParen => depth += 1,
4862 Token::RParen => depth -= 1,
4863 Token::Eof => break,
4864 _ => {}
4865 }
4866 self.advance();
4867 }
4868 }
4869 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
4871 || matches!(self.peek(), Token::Asc | Token::Desc)
4872 {
4873 self.advance();
4874 }
4875 if matches!(self.peek(), Token::Comma) {
4876 self.advance();
4877 continue;
4878 }
4879 break;
4880 }
4881 if matches!(self.peek(), Token::RParen) {
4882 self.advance();
4883 }
4884 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
4887 self.advance();
4888 }
4889 if cols.is_empty() {
4890 return Ok(None);
4891 }
4892 if is_unique {
4893 Ok(Some(crate::ast::TableConstraint::Unique {
4899 name: idx_name,
4900 columns: cols,
4901 nulls_not_distinct: false,
4902 }))
4903 } else if is_fulltext {
4904 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
4910 name: idx_name,
4911 columns: cols,
4912 }))
4913 } else if is_spatial {
4914 Ok(None)
4917 } else {
4918 Ok(Some(crate::ast::TableConstraint::Index {
4921 name: idx_name,
4922 columns: cols,
4923 }))
4924 }
4925 }
4926
4927 fn consume_mysql_table_options(&mut self) {
4932 loop {
4933 let name_lc = match self.peek().clone() {
4937 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
4938 Token::Default => alloc::string::String::from("default"),
4939 _ => break,
4940 };
4941 let known = matches!(
4942 name_lc.as_str(),
4943 "engine"
4944 | "default"
4945 | "charset"
4946 | "collate"
4947 | "auto_increment"
4948 | "row_format"
4949 | "comment"
4950 | "pack_keys"
4951 | "stats_persistent"
4952 | "stats_auto_recalc"
4953 | "stats_sample_pages"
4954 | "key_block_size"
4955 | "tablespace"
4956 | "min_rows"
4957 | "max_rows"
4958 | "checksum"
4959 | "delay_key_write"
4960 | "insert_method"
4961 | "data"
4962 | "index"
4963 | "encryption"
4964 | "compression"
4965 );
4966 if !known {
4967 break;
4968 }
4969 self.advance(); if name_lc == "default" {
4973 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4974 self.advance();
4975 }
4976 }
4977 if matches!(self.peek(), Token::Eq) {
4978 self.advance();
4979 }
4980 match self.peek() {
4981 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
4982 self.advance();
4983 }
4984 _ => {}
4985 }
4986 }
4987 }
4988
4989 fn peek_table_level_pk_start(&self) -> bool {
4994 let cur = self.peek();
4995 let nxt = self.tokens.get(self.pos + 1);
4996 let nxt2 = self.tokens.get(self.pos + 2);
4997 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
4998 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
4999 let is_lparen = matches!(nxt2, Some(Token::LParen));
5000 is_primary && is_key && is_lparen
5001 }
5002
5003 fn peek_table_level_unique_start(&self) -> bool {
5007 let cur = self.peek();
5008 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5009 if !is_unique {
5010 return false;
5011 }
5012 let n1 = self.tokens.get(self.pos + 1);
5013 if matches!(n1, Some(Token::LParen)) {
5015 return true;
5016 }
5017 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5019 if !is_nulls {
5020 return false;
5021 }
5022 let n2 = self.tokens.get(self.pos + 2);
5023 let n3 = self.tokens.get(self.pos + 3);
5024 let n4 = self.tokens.get(self.pos + 4);
5025 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5027 return true;
5028 }
5029 if matches!(n2, Some(Token::Not))
5031 && matches!(n3, Some(Token::Distinct))
5032 && matches!(n4, Some(Token::LParen))
5033 {
5034 return true;
5035 }
5036 false
5037 }
5038
5039 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5040 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5043 Ok(crate::ast::TableConstraint::PrimaryKey {
5044 name: None,
5045 columns,
5046 })
5047 }
5048
5049 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5050 self.advance(); let mut nulls_not_distinct = false;
5055 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5056 let n1 = self.tokens.get(self.pos + 1);
5057 let n2 = self.tokens.get(self.pos + 2);
5058 let is_not = matches!(n1, Some(Token::Not));
5059 let is_distinct = matches!(n2, Some(Token::Distinct));
5060 if is_not && is_distinct {
5061 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5065 } else if matches!(n1, Some(Token::Distinct)) {
5066 self.advance(); self.advance(); }
5069 }
5070 let columns = self.parse_paren_ident_list("UNIQUE")?;
5071 Ok(crate::ast::TableConstraint::Unique {
5072 name: None,
5073 columns,
5074 nulls_not_distinct,
5075 })
5076 }
5077
5078 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5082 self.advance(); if !matches!(self.peek(), Token::LParen) {
5084 return Err(self.err(alloc::format!(
5085 "expected '(' after CHECK, got {:?}",
5086 self.peek()
5087 )));
5088 }
5089 self.advance();
5090 let expr = self.parse_expr(0)?;
5091 if !matches!(self.peek(), Token::RParen) {
5092 return Err(self.err(alloc::format!(
5093 "expected ')' to close CHECK predicate, got {:?}",
5094 self.peek()
5095 )));
5096 }
5097 self.advance();
5098 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5099 }
5100
5101 fn peek_table_level_check_start(&self) -> bool {
5103 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5104 }
5105
5106 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5107 if !matches!(self.peek(), Token::LParen) {
5108 return Err(self.err(alloc::format!(
5109 "expected '(' after {ctx}, got {:?}",
5110 self.peek()
5111 )));
5112 }
5113 self.advance();
5114 let mut out = Vec::new();
5115 loop {
5116 out.push(self.expect_ident_like()?);
5117 match self.peek() {
5118 Token::Comma => {
5119 self.advance();
5120 }
5121 Token::RParen => {
5122 self.advance();
5123 break;
5124 }
5125 other => {
5126 return Err(self.err(alloc::format!(
5127 "expected ',' or ')' in {ctx} list, got {other:?}"
5128 )));
5129 }
5130 }
5131 }
5132 if out.is_empty() {
5133 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5134 }
5135 Ok(out)
5136 }
5137
5138 fn peek_constraint_or_fk_start(&self) -> bool {
5143 let is_constraint_kw = matches!(
5144 self.peek(),
5145 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5146 );
5147 let is_foreign_kw = matches!(
5148 self.peek(),
5149 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5150 );
5151 is_constraint_kw || is_foreign_kw
5152 }
5153
5154 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5158 let mut name: Option<String> = None;
5159 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5160 self.advance();
5161 name = Some(self.expect_ident_like()?);
5162 }
5163 match self.advance() {
5165 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5166 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5167 }
5168 match self.advance() {
5170 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5171 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5172 }
5173 if !matches!(self.peek(), Token::LParen) {
5175 return Err(self.err(format!(
5176 "expected '(' after FOREIGN KEY, got {:?}",
5177 self.peek()
5178 )));
5179 }
5180 self.advance();
5181 let mut columns = Vec::new();
5182 loop {
5183 columns.push(self.expect_ident_like()?);
5184 match self.peek() {
5185 Token::Comma => {
5186 self.advance();
5187 }
5188 Token::RParen => {
5189 self.advance();
5190 break;
5191 }
5192 other => {
5193 return Err(self.err(format!(
5194 "expected ',' or ')' in FK column list, got {other:?}"
5195 )));
5196 }
5197 }
5198 }
5199 if columns.is_empty() {
5200 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5201 }
5202 let (parent_table, parent_columns, on_delete, on_update) =
5203 self.parse_references_tail(columns.len())?;
5204 Ok(ForeignKeyConstraint {
5205 name,
5206 columns,
5207 parent_table,
5208 parent_columns,
5209 on_delete,
5210 on_update,
5211 })
5212 }
5213
5214 fn parse_references_tail(
5219 &mut self,
5220 expected_arity: usize,
5221 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5222 match self.advance() {
5223 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5224 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5225 }
5226 let parent_table = self.expect_ident_like()?;
5227 let mut parent_columns: Vec<String> = Vec::new();
5228 if matches!(self.peek(), Token::LParen) {
5229 self.advance();
5230 loop {
5231 parent_columns.push(self.expect_ident_like()?);
5232 match self.peek() {
5233 Token::Comma => {
5234 self.advance();
5235 }
5236 Token::RParen => {
5237 self.advance();
5238 break;
5239 }
5240 other => {
5241 return Err(self.err(format!(
5242 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5243 )));
5244 }
5245 }
5246 }
5247 }
5248 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5249 return Err(self.err(format!(
5250 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5251 expected_arity,
5252 parent_columns.len()
5253 )));
5254 }
5255 let mut on_delete = FkAction::Restrict;
5268 let mut on_update = FkAction::Restrict;
5269 let mut seen_on_delete = false;
5270 let mut seen_on_update = false;
5271 loop {
5272 let before = self.pos;
5274 self.consume_optional_deferrable_clauses()?;
5275 if self.pos != before {
5276 continue;
5277 }
5278 if !matches!(self.peek(), Token::On) {
5280 break;
5281 }
5282 self.advance();
5283 let which = self.advance();
5284 let action = self.parse_fk_action()?;
5285 match which {
5286 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5287 if seen_on_delete {
5288 return Err(self.err("ON DELETE specified twice".into()));
5289 }
5290 seen_on_delete = true;
5291 on_delete = action;
5292 }
5293 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5294 if seen_on_update {
5295 return Err(self.err("ON UPDATE specified twice".into()));
5296 }
5297 seen_on_update = true;
5298 on_update = action;
5299 }
5300 other => {
5301 return Err(
5302 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5303 );
5304 }
5305 }
5306 }
5307 Ok((parent_table, parent_columns, on_delete, on_update))
5308 }
5309
5310 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5313 match self.advance() {
5314 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5315 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5316 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5317 Token::Null => Ok(FkAction::SetNull),
5318 Token::Default => Ok(FkAction::SetDefault),
5319 other => Err(self.err(format!(
5320 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5321 ))),
5322 },
5323 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5324 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5325 other => Err(self.err(format!(
5326 "expected ACTION after NO in FK action, got {other:?}"
5327 ))),
5328 },
5329 other => Err(self.err(format!(
5330 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5331 ))),
5332 }
5333 }
5334
5335 fn consume_if_not_exists(&mut self) -> bool {
5338 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5342 if !looks_like_if {
5343 return false;
5344 }
5345 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5348 return false;
5349 }
5350 if !matches!(
5351 self.tokens.get(self.pos + 2),
5352 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5353 ) {
5354 return false;
5355 }
5356 self.advance(); self.advance(); self.advance(); true
5360 }
5361
5362 fn consume_if_exists(&mut self) -> bool {
5366 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5367 if !looks_like_if {
5368 return false;
5369 }
5370 if !matches!(
5371 self.tokens.get(self.pos + 1),
5372 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5373 ) {
5374 return false;
5375 }
5376 self.advance(); self.advance(); true
5379 }
5380
5381 fn consume_optional_index_column_qualifiers(&mut self) {
5387 loop {
5388 match self.peek() {
5389 Token::Asc | Token::Desc => {
5390 self.advance();
5391 }
5392 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5393 let look = self.tokens.get(self.pos + 1);
5394 if matches!(
5395 look,
5396 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5397 || k.eq_ignore_ascii_case("last")
5398 ) {
5399 self.advance();
5400 self.advance();
5401 } else {
5402 break;
5403 }
5404 }
5405 _ => break,
5406 }
5407 }
5408 }
5409
5410 fn parse_create_index_stmt_after_create(
5411 &mut self,
5412 is_unique: bool,
5413 ) -> Result<Statement, ParseError> {
5414 debug_assert!(matches!(self.peek(), Token::Index));
5416 self.advance();
5417 let if_not_exists = self.consume_if_not_exists();
5418 let name = self.expect_ident_like()?;
5419 if !matches!(self.peek(), Token::On) {
5420 return Err(self.err(format!(
5421 "expected ON after CREATE INDEX <name>, got {:?}",
5422 self.peek()
5423 )));
5424 }
5425 self.advance();
5426 let table = self.expect_ident_like()?;
5427 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5432 self.advance();
5433 let m = self.expect_ident_like()?;
5434 match m.to_ascii_lowercase().as_str() {
5435 "hnsw" => IndexMethod::Hnsw,
5436 "btree" => IndexMethod::BTree,
5437 "brin" => IndexMethod::Brin,
5438 "gin" => IndexMethod::Gin,
5443 "gist" | "spgist" | "hash" => IndexMethod::BTree,
5452 "ivfflat" => IndexMethod::Hnsw,
5460 other => {
5461 return Err(self.err(alloc::format!(
5462 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
5463 )));
5464 }
5465 }
5466 } else {
5467 IndexMethod::BTree
5468 };
5469 if !matches!(self.peek(), Token::LParen) {
5470 return Err(self.err(format!(
5471 "expected '(' before indexed column, got {:?}",
5472 self.peek()
5473 )));
5474 }
5475 self.advance();
5476 let mut opclass: Option<String> = None;
5485 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
5486 Token::Ident(s) | Token::QuotedIdent(s)
5493 if matches!(
5494 self.tokens.get(self.pos + 1),
5495 Some(Token::RParen | Token::Comma)
5496 ) =>
5497 {
5498 self.advance();
5499 (s, None)
5500 }
5501 Token::Ident(s) | Token::QuotedIdent(s)
5511 if matches!(
5512 self.tokens.get(self.pos + 1),
5513 Some(Token::Ident(op) | Token::QuotedIdent(op))
5514 if is_vector_opclass_name(op)
5515 ) =>
5516 {
5517 self.advance(); let op_tok = self.advance();
5521 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5522 opclass = Some(op.to_ascii_lowercase());
5523 }
5524 (s, None)
5525 }
5526 Token::Ident(_) | Token::QuotedIdent(_) => {
5527 let key_expr = self.parse_expr(0)?;
5528 let primary = extract_first_column(&key_expr).ok_or_else(|| {
5529 self.err("expression index key must reference at least one column".into())
5530 })?;
5531 (primary, Some(key_expr))
5532 }
5533 other => {
5534 return Err(self.err(format!(
5535 "expected column ident or expression, got {other:?}"
5536 )));
5537 }
5538 };
5539 let mut extra_columns: Vec<String> = Vec::new();
5548 self.consume_optional_index_column_qualifiers();
5550 while matches!(self.peek(), Token::Comma) {
5551 self.advance();
5552 let extra = self.expect_ident_like()?;
5553 self.consume_optional_index_column_qualifiers();
5554 extra_columns.push(extra);
5555 }
5556 if !matches!(self.peek(), Token::RParen) {
5557 return Err(self.err(format!(
5558 "expected ')' after indexed column / expression, got {:?}",
5559 self.peek()
5560 )));
5561 }
5562 self.advance();
5563 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
5567 {
5568 self.advance();
5569 if !matches!(self.peek(), Token::LParen) {
5570 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
5571 }
5572 self.advance();
5573 let mut cols = Vec::new();
5574 loop {
5575 cols.push(self.expect_ident_like()?);
5576 match self.peek() {
5577 Token::Comma => {
5578 self.advance();
5579 }
5580 Token::RParen => {
5581 self.advance();
5582 break;
5583 }
5584 other => {
5585 return Err(self.err(format!(
5586 "expected ',' or ')' in INCLUDE list, got {other:?}"
5587 )));
5588 }
5589 }
5590 }
5591 cols
5592 } else {
5593 Vec::new()
5594 };
5595 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
5601 self.advance();
5602 if !matches!(self.peek(), Token::LParen) {
5603 return Err(self.err(format!(
5604 "expected '(' after WITH in CREATE INDEX, got {:?}",
5605 self.peek()
5606 )));
5607 }
5608 self.advance();
5609 loop {
5610 if matches!(self.peek(), Token::RParen) {
5611 self.advance();
5612 break;
5613 }
5614 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
5617 self.advance();
5618 let _ = self.advance(); }
5620 match self.peek() {
5621 Token::Comma => {
5622 self.advance();
5623 }
5624 Token::RParen => {
5625 self.advance();
5626 break;
5627 }
5628 other => {
5629 return Err(self.err(format!(
5630 "expected ',' or ')' in WITH (…) clause, got {other:?}"
5631 )));
5632 }
5633 }
5634 }
5635 }
5636 let partial_predicate = if matches!(self.peek(), Token::Where) {
5638 self.advance();
5639 Some(self.parse_expr(0)?)
5640 } else {
5641 None
5642 };
5643 if is_unique && !matches!(method, IndexMethod::BTree) {
5648 return Err(self.err(alloc::format!(
5649 "UNIQUE is only supported on BTree indexes, got USING {:?}",
5650 method
5651 )));
5652 }
5653 Ok(Statement::CreateIndex(CreateIndexStatement {
5654 name,
5655 table,
5656 column,
5657 method,
5658 if_not_exists,
5659 included_columns,
5660 partial_predicate,
5661 extra_columns: extra_columns.clone(),
5662 expression,
5663 is_unique,
5664 opclass,
5665 }))
5666 }
5667
5668 fn parse_column_def_with_fk(
5673 &mut self,
5674 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
5675 let col = self.parse_column_def()?;
5676 let inline_references = matches!(
5678 self.peek(),
5679 Token::Ident(s) if s.eq_ignore_ascii_case("references")
5680 );
5681 if !inline_references {
5682 return Ok((col, None));
5683 }
5684 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
5685 let fk = ForeignKeyConstraint {
5686 name: None,
5687 columns: vec![col.name.clone()],
5688 parent_table,
5689 parent_columns,
5690 on_delete,
5691 on_update,
5692 };
5693 Ok((col, Some(fk)))
5694 }
5695
5696 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
5704 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
5705 Ok(ty)
5706 }
5707
5708 #[allow(clippy::type_complexity)]
5709 fn parse_type_with_implied_flags(
5710 &mut self,
5711 ) -> Result<
5712 (
5713 ColumnTypeName,
5714 bool,
5715 bool,
5716 Option<String>,
5717 Collation,
5718 bool,
5719 Option<Vec<String>>,
5723 Option<Vec<String>>,
5726 ),
5727 ParseError,
5728 > {
5729 let ty_ident = match self.advance() {
5730 Token::Ident(s) => s,
5731 other => {
5732 return Err(ParseError {
5733 message: format!("expected column type, got {other:?}"),
5734 token_pos: self.pos.saturating_sub(1),
5735 });
5736 }
5737 };
5738 let mut implied_auto_increment = false;
5739 let mut implied_not_null = false;
5740 let mut user_type_ref: Option<String> = None;
5741 let mut inline_enum_variants: Option<Vec<String>> = None;
5746 let mut inline_set_variants: Option<Vec<String>> = None;
5748 let mut ty = match ty_ident.as_str() {
5749 "smallserial" | "serial2" => {
5751 implied_auto_increment = true;
5752 implied_not_null = true;
5753 ColumnTypeName::SmallInt
5754 }
5755 "serial" | "serial4" => {
5756 implied_auto_increment = true;
5757 implied_not_null = true;
5758 ColumnTypeName::Int
5759 }
5760 "bigserial" | "serial8" => {
5761 implied_auto_increment = true;
5762 implied_not_null = true;
5763 ColumnTypeName::BigInt
5764 }
5765 "smallint" => {
5771 self.consume_optional_paren_size();
5776 ColumnTypeName::SmallInt
5777 }
5778 "tinyint" => {
5789 let width = self.peek_optional_paren_size_value();
5790 self.consume_optional_paren_size();
5791 if width == Some(1) {
5792 ColumnTypeName::Bool
5793 } else {
5794 ColumnTypeName::SmallInt
5795 }
5796 }
5797 "int" | "integer" | "mediumint" => {
5798 self.consume_optional_paren_size();
5799 ColumnTypeName::Int
5800 }
5801 "bigint" => {
5802 self.consume_optional_paren_size();
5803 ColumnTypeName::BigInt
5804 }
5805 "float" | "double" | "real" => {
5810 if ty_ident.eq_ignore_ascii_case("double")
5811 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
5812 {
5813 self.advance();
5814 }
5815 ColumnTypeName::Float
5816 }
5817 "float4" | "float8" => ColumnTypeName::Float,
5819 "text" => ColumnTypeName::Text,
5820 "bool" | "boolean" => ColumnTypeName::Bool,
5821 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
5822 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
5823 "vector" => {
5824 let dim = self.parse_paren_size("VECTOR")?;
5825 let encoding = self.parse_optional_vector_encoding()?;
5826 ColumnTypeName::Vector { dim, encoding }
5827 }
5828 "numeric" => {
5829 let (precision, scale) = self.parse_optional_numeric_params()?;
5830 ColumnTypeName::Numeric(precision, scale)
5831 }
5832 "date" => ColumnTypeName::Date,
5833 "timestamp" | "datetime" => {
5836 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
5842 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
5843 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
5844 {
5845 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
5849 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
5850 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
5851 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
5852 {
5853 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
5857 } else {
5858 self.consume_optional_paren_size();
5862 ColumnTypeName::Timestamp
5863 }
5864 }
5865 "timestamptz" => ColumnTypeName::Timestamptz,
5869 "json" => ColumnTypeName::Json,
5874 "jsonb" => ColumnTypeName::Jsonb,
5875 "bytea" | "bytes" => ColumnTypeName::Bytes,
5881 "inet" | "cidr" | "macaddr" => ColumnTypeName::Text,
5891 "tsvector" => ColumnTypeName::TsVector,
5896 "tsquery" => ColumnTypeName::TsQuery,
5897 "uuid" => ColumnTypeName::Uuid,
5901 "time" => ColumnTypeName::Time,
5904 "year" => ColumnTypeName::Year,
5907 "timetz" => ColumnTypeName::TimeTz,
5910 "money" => ColumnTypeName::Money,
5913 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
5915 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
5916 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
5917 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
5918 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
5919 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
5920 "hstore" => ColumnTypeName::Hstore,
5922 "enum" => {
5928 if !matches!(self.peek(), Token::LParen) {
5930 return Err(self.err(alloc::format!(
5931 "expected '(' after ENUM, got {:?}",
5932 self.peek()
5933 )));
5934 }
5935 self.advance();
5936 let mut variants: Vec<String> = Vec::new();
5937 loop {
5938 match self.advance() {
5939 Token::String(s) => variants.push(s),
5940 other => {
5941 return Err(self.err(alloc::format!(
5942 "ENUM(...) expects string literal variants, got {other:?}"
5943 )));
5944 }
5945 }
5946 match self.peek() {
5947 Token::Comma => {
5948 self.advance();
5949 continue;
5950 }
5951 Token::RParen => {
5952 self.advance();
5953 break;
5954 }
5955 other => {
5956 return Err(self.err(alloc::format!(
5957 "expected ',' or ')' in ENUM(...), got {other:?}"
5958 )));
5959 }
5960 }
5961 }
5962 if variants.is_empty() {
5963 return Err(self.err("ENUM(...) must declare at least one variant".into()));
5964 }
5965 inline_enum_variants = Some(variants);
5966 ColumnTypeName::Text
5969 }
5970 "set" => {
5974 if !matches!(self.peek(), Token::LParen) {
5975 return Err(self.err(alloc::format!(
5976 "expected '(' after SET, got {:?}",
5977 self.peek()
5978 )));
5979 }
5980 self.advance();
5981 let mut variants: Vec<String> = Vec::new();
5982 loop {
5983 match self.advance() {
5984 Token::String(s) => variants.push(s),
5985 other => {
5986 return Err(self.err(alloc::format!(
5987 "SET(...) expects string literal variants, got {other:?}"
5988 )));
5989 }
5990 }
5991 match self.peek() {
5992 Token::Comma => {
5993 self.advance();
5994 continue;
5995 }
5996 Token::RParen => {
5997 self.advance();
5998 break;
5999 }
6000 other => {
6001 return Err(self.err(alloc::format!(
6002 "expected ',' or ')' in SET(...), got {other:?}"
6003 )));
6004 }
6005 }
6006 }
6007 if variants.is_empty() {
6008 return Err(self.err("SET(...) must declare at least one variant".into()));
6009 }
6010 inline_set_variants = Some(variants);
6011 ColumnTypeName::Text
6012 }
6013 _other => {
6014 user_type_ref = Some(ty_ident.clone());
6020 ColumnTypeName::Text
6021 }
6022 };
6023 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6033 {
6034 self.advance();
6035 true
6036 } else {
6037 false
6038 };
6039 let mut collation = Collation::Binary;
6055 loop {
6056 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6057 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6058 {
6059 self.advance(); self.advance(); if matches!(
6062 self.peek(),
6063 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6064 ) {
6065 self.advance();
6066 }
6067 continue;
6068 }
6069 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6070 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6077 match this.peek().clone() {
6078 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6079 this.advance();
6080 Some(s)
6081 }
6082 Token::Default => {
6083 this.advance();
6084 Some(alloc::string::String::from("default"))
6085 }
6086 _ => None,
6087 }
6088 };
6089 let raw = if let Some(head) = read_collation_atom(self) {
6090 if matches!(self.peek(), Token::Dot) {
6092 self.advance();
6093 let tail = read_collation_atom(self).unwrap_or_default();
6094 alloc::format!("{head}.{tail}")
6095 } else {
6096 head
6097 }
6098 } else {
6099 alloc::string::String::new()
6100 };
6101 if !raw.is_empty() {
6102 let parsed = Collation::from_collation_name(&raw);
6103 if parsed != Collation::Binary {
6109 collation = parsed;
6110 }
6111 }
6112 continue;
6113 }
6114 break;
6115 }
6116 if matches!(self.peek(), Token::LBracket) {
6121 self.advance();
6122 if !matches!(self.peek(), Token::RBracket) {
6123 return Err(self.err(alloc::format!(
6124 "TEXT[] takes no dimension; got {:?}",
6125 self.peek()
6126 )));
6127 }
6128 self.advance();
6129 ty = match ty {
6133 ColumnTypeName::Text => ColumnTypeName::TextArray,
6134 ColumnTypeName::Int => ColumnTypeName::IntArray,
6135 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6136 other => {
6137 return Err(self.err(alloc::format!(
6138 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
6139 )));
6140 }
6141 };
6142 if matches!(self.peek(), Token::LBracket) {
6145 self.advance();
6146 if !matches!(self.peek(), Token::RBracket) {
6147 return Err(self.err(alloc::format!(
6148 "TYPE[][] second dimension takes no size; got {:?}",
6149 self.peek()
6150 )));
6151 }
6152 self.advance();
6153 ty = match ty {
6154 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6155 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6156 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6157 other => {
6158 return Err(self.err(alloc::format!(
6159 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6160 TEXT[][] only; got {other:?}"
6161 )));
6162 }
6163 };
6164 }
6165 }
6166 Ok((
6167 ty,
6168 implied_auto_increment,
6169 implied_not_null,
6170 user_type_ref,
6171 collation,
6172 is_unsigned,
6173 inline_enum_variants,
6174 inline_set_variants,
6175 ))
6176 }
6177
6178 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6179 if let Token::Ident(s) = self.peek()
6188 && [
6189 "unique",
6190 "primary",
6191 "foreign",
6192 "constraint",
6193 "check",
6194 "references",
6195 "exclude",
6196 ]
6197 .iter()
6198 .any(|kw| s.eq_ignore_ascii_case(kw))
6199 {
6200 return Err(self.err(alloc::format!(
6201 "unexpected reserved keyword '{s}' at start of column definition \
6202 (malformed table constraint?)"
6203 )));
6204 }
6205 let name = self.expect_ident_like()?;
6206 let (
6207 ty,
6208 implied_auto_increment,
6209 implied_not_null,
6210 user_type_ref,
6211 collation,
6212 is_unsigned,
6213 inline_enum_variants,
6214 inline_set_variants,
6215 ) = self.parse_type_with_implied_flags()?;
6216 let mut default: Option<Expr> = None;
6220 let mut nullable = !implied_not_null;
6221 let mut nullability_seen = implied_not_null;
6222 let mut auto_increment = implied_auto_increment;
6223 let mut is_primary_key = false;
6224 let mut is_unique = false;
6225 let mut check: Option<Expr> = None;
6226 let mut on_update_runtime: Option<Expr> = None;
6227 loop {
6228 if matches!(self.peek(), Token::On)
6233 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6234 {
6235 self.advance(); self.advance(); let next = self.peek().clone();
6239 match next {
6240 Token::Ident(s) | Token::QuotedIdent(s)
6241 if s.eq_ignore_ascii_case("current_timestamp") =>
6242 {
6243 self.advance();
6244 if matches!(self.peek(), Token::LParen) {
6246 self.advance();
6247 if !matches!(self.peek(), Token::Integer(_)) {
6248 return Err(self.err(alloc::format!(
6249 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6250 self.peek()
6251 )));
6252 }
6253 self.advance();
6254 if !matches!(self.peek(), Token::RParen) {
6255 return Err(self.err(alloc::format!(
6256 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6257 self.peek()
6258 )));
6259 }
6260 self.advance();
6261 }
6262 on_update_runtime = Some(Expr::FunctionCall {
6263 name: "now".into(),
6264 args: Vec::new(),
6265 });
6266 continue;
6267 }
6268 other => {
6269 return Err(self.err(alloc::format!(
6270 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6271 )));
6272 }
6273 }
6274 }
6275 if matches!(self.peek(), Token::Default) {
6276 if default.is_some() {
6277 return Err(self.err("DEFAULT specified twice".into()));
6278 }
6279 self.advance();
6280 default = Some(self.parse_expr(0)?);
6281 continue;
6282 }
6283 if matches!(self.peek(), Token::Not) {
6284 if nullability_seen {
6285 return Err(self.err("NOT NULL specified twice".into()));
6286 }
6287 self.advance();
6288 if !matches!(self.peek(), Token::Null) {
6289 return Err(self.err(format!(
6290 "expected NULL after NOT in column def, got {:?}",
6291 self.peek()
6292 )));
6293 }
6294 self.advance();
6295 nullable = false;
6296 nullability_seen = true;
6297 continue;
6298 }
6299 if matches!(self.peek(), Token::Null) {
6305 if nullability_seen && !nullable {
6306 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6307 }
6308 self.advance();
6309 nullable = true;
6310 nullability_seen = true;
6311 continue;
6312 }
6313 if let Token::Ident(s) = self.peek()
6316 && (s.eq_ignore_ascii_case("auto_increment")
6317 || s.eq_ignore_ascii_case("autoincrement"))
6318 {
6319 if auto_increment {
6320 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6321 }
6322 self.advance();
6323 auto_increment = true;
6324 continue;
6325 }
6326 if let Token::Ident(s) = self.peek()
6331 && s.eq_ignore_ascii_case("primary")
6332 {
6333 if is_primary_key {
6334 return Err(self.err("PRIMARY KEY specified twice".into()));
6335 }
6336 let next = self.tokens.get(self.pos + 1);
6338 let next_is_key = matches!(
6339 next,
6340 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6341 );
6342 if !next_is_key {
6343 return Err(self.err(format!(
6344 "expected KEY after PRIMARY in column def, got {:?}",
6345 next
6346 )));
6347 }
6348 self.advance(); self.advance(); is_primary_key = true;
6351 if nullability_seen && nullable {
6352 return Err(self.err(
6353 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6354 ));
6355 }
6356 nullable = false;
6357 nullability_seen = true;
6358 continue;
6359 }
6360 if let Token::Ident(s) = self.peek()
6364 && s.eq_ignore_ascii_case("unique")
6365 {
6366 if is_unique {
6367 return Err(self.err("UNIQUE specified twice".into()));
6368 }
6369 self.advance();
6370 is_unique = true;
6371 continue;
6372 }
6373 if let Token::Ident(s) = self.peek()
6378 && s.eq_ignore_ascii_case("check")
6379 {
6380 self.advance();
6381 if !matches!(self.peek(), Token::LParen) {
6382 return Err(self.err(alloc::format!(
6383 "expected '(' after CHECK in column def, got {:?}",
6384 self.peek()
6385 )));
6386 }
6387 self.advance();
6388 let pred = self.parse_expr(0)?;
6389 if !matches!(self.peek(), Token::RParen) {
6390 return Err(self.err(alloc::format!(
6391 "expected ')' to close CHECK predicate, got {:?}",
6392 self.peek()
6393 )));
6394 }
6395 self.advance();
6396 check = Some(match check.take() {
6397 Some(prev) => Expr::Binary {
6398 op: BinOp::And,
6399 lhs: Box::new(prev),
6400 rhs: Box::new(pred),
6401 },
6402 None => pred,
6403 });
6404 continue;
6405 }
6406 break;
6407 }
6408 Ok(ColumnDef {
6409 name,
6410 ty,
6411 nullable,
6412 default,
6413 auto_increment,
6414 is_primary_key,
6415 is_unique,
6416 check,
6417 user_type_ref,
6418 on_update_runtime,
6419 collation,
6420 is_unsigned,
6421 inline_enum_variants,
6422 inline_set_variants,
6423 })
6424 }
6425
6426 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6430 if !matches!(self.peek(), Token::LParen) {
6431 return Ok((0, 0));
6435 }
6436 self.advance();
6437 let precision = match self.advance() {
6438 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6439 other => {
6440 return Err(ParseError {
6441 message: format!(
6442 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6443 ),
6444 token_pos: self.pos.saturating_sub(1),
6445 });
6446 }
6447 };
6448 let scale = if matches!(self.peek(), Token::Comma) {
6449 self.advance();
6450 match self.advance() {
6451 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6452 u8::try_from(n).expect("range-checked")
6453 }
6454 other => {
6455 return Err(ParseError {
6456 message: format!(
6457 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6458 ),
6459 token_pos: self.pos.saturating_sub(1),
6460 });
6461 }
6462 }
6463 } else {
6464 0
6465 };
6466 if !matches!(self.peek(), Token::RParen) {
6467 return Err(self.err(format!(
6468 "expected ')' to close NUMERIC params, got {:?}",
6469 self.peek()
6470 )));
6471 }
6472 self.advance();
6473 Ok((precision, scale))
6474 }
6475
6476 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6484 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6485 return Ok(VecEncoding::F32);
6486 }
6487 let n1 = self.tokens.get(self.pos + 1);
6493 let next_is_encoding = matches!(
6494 n1,
6495 Some(Token::Ident(s))
6496 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6497 );
6498 if !next_is_encoding {
6499 return Ok(VecEncoding::F32);
6500 }
6501 self.advance();
6502 let enc_ident = match self.advance() {
6503 Token::Ident(s) => s,
6504 other => {
6505 return Err(self.err(format!(
6506 "expected vector encoding after USING, got {other:?}"
6507 )));
6508 }
6509 };
6510 match enc_ident.to_ascii_lowercase().as_str() {
6511 "sq8" => Ok(VecEncoding::Sq8),
6512 "half" => Ok(VecEncoding::F16),
6515 other => Err(self.err(format!(
6516 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6517 ))),
6518 }
6519 }
6520
6521 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6527 if !matches!(self.peek(), Token::LParen) {
6528 return None;
6529 }
6530 let next = self.tokens.get(self.pos + 1)?;
6531 let n = match next {
6532 Token::Integer(n) => *n,
6533 _ => return None,
6534 };
6535 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6536 return None;
6537 }
6538 Some(n)
6539 }
6540
6541 fn consume_optional_paren_size(&mut self) {
6545 if !matches!(self.peek(), Token::LParen) {
6546 return;
6547 }
6548 self.advance();
6549 let mut depth = 1usize;
6551 while depth > 0 {
6552 match self.peek() {
6553 Token::LParen => depth += 1,
6554 Token::RParen => depth -= 1,
6555 Token::Eof => return,
6556 _ => {}
6557 }
6558 self.advance();
6559 }
6560 }
6561
6562 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6563 if !matches!(self.peek(), Token::LParen) {
6564 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6565 }
6566 self.advance();
6567 let n = match self.advance() {
6568 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6569 message: format!("{label} size too large: {n}"),
6570 token_pos: self.pos.saturating_sub(1),
6571 })?,
6572 other => {
6573 return Err(ParseError {
6574 message: format!("expected positive integer {label} size, got {other:?}"),
6575 token_pos: self.pos.saturating_sub(1),
6576 });
6577 }
6578 };
6579 if !matches!(self.peek(), Token::RParen) {
6580 return Err(self.err(format!(
6581 "expected ')' after {label} size, got {:?}",
6582 self.peek()
6583 )));
6584 }
6585 self.advance();
6586 Ok(n)
6587 }
6588
6589 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6590 debug_assert!(matches!(self.peek(), Token::Insert));
6591 self.advance();
6592 if !matches!(self.peek(), Token::Into) {
6593 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6594 }
6595 self.advance();
6596 let table = self.expect_ident_like()?;
6597 let columns = if matches!(self.peek(), Token::LParen) {
6599 self.advance();
6600 let mut names = Vec::new();
6601 loop {
6602 names.push(self.expect_ident_like()?);
6603 match self.peek() {
6604 Token::Comma => {
6605 self.advance();
6606 }
6607 Token::RParen => {
6608 self.advance();
6609 break;
6610 }
6611 other => {
6612 return Err(self.err(format!(
6613 "expected ',' or ')' in INSERT column list, got {other:?}"
6614 )));
6615 }
6616 }
6617 }
6618 Some(names)
6619 } else {
6620 None
6621 };
6622 if matches!(self.peek(), Token::Select) {
6625 let select_stmt = match self.parse_select_stmt()? {
6626 Statement::Select(s) => s,
6627 other => {
6628 return Err(self.err(alloc::format!(
6629 "expected SELECT after INSERT INTO ... target, got {other:?}"
6630 )));
6631 }
6632 };
6633 let on_conflict = self.parse_optional_on_conflict()?;
6634 let returning = self.parse_optional_returning()?;
6635 return Ok(Statement::Insert(InsertStatement {
6636 table,
6637 columns,
6638 rows: Vec::new(),
6639 select_source: Some(Box::new(select_stmt)),
6640 on_conflict,
6641 returning,
6642 }));
6643 }
6644 if !matches!(self.peek(), Token::Values) {
6645 return Err(self.err(format!(
6646 "expected VALUES or SELECT after table name, got {:?}",
6647 self.peek()
6648 )));
6649 }
6650 self.advance();
6651 if !matches!(self.peek(), Token::LParen) {
6652 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
6653 }
6654 let mut rows = Vec::new();
6655 loop {
6656 if !matches!(self.peek(), Token::LParen) {
6658 return Err(self.err(format!(
6659 "expected '(' for next VALUES tuple, got {:?}",
6660 self.peek()
6661 )));
6662 }
6663 self.advance();
6664 let mut tuple = Vec::new();
6665 loop {
6666 tuple.push(self.parse_expr(0)?);
6667 match self.peek() {
6668 Token::Comma => {
6669 self.advance();
6670 }
6671 Token::RParen => {
6672 self.advance();
6673 break;
6674 }
6675 other => {
6676 return Err(self.err(format!(
6677 "expected ',' or ')' in VALUES tuple, got {other:?}"
6678 )));
6679 }
6680 }
6681 }
6682 if tuple.is_empty() {
6683 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
6684 }
6685 rows.push(tuple);
6686 if matches!(self.peek(), Token::Comma) {
6688 self.advance();
6689 } else {
6690 break;
6691 }
6692 }
6693 let on_conflict = self.parse_optional_on_conflict()?;
6694 let returning = self.parse_optional_returning()?;
6695 Ok(Statement::Insert(InsertStatement {
6696 table,
6697 columns,
6698 rows,
6699 select_source: None,
6700 on_conflict,
6701 returning,
6702 }))
6703 }
6704
6705 fn parse_optional_on_conflict(
6710 &mut self,
6711 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
6712 if !matches!(self.peek(), Token::On) {
6713 return Ok(None);
6714 }
6715 let next_is_conflict = matches!(
6718 self.tokens.get(self.pos + 1),
6719 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
6720 );
6721 if !next_is_conflict {
6722 return Ok(None);
6723 }
6724 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
6728 if matches!(self.peek(), Token::LParen) {
6729 self.advance();
6730 loop {
6731 target_columns.push(self.expect_ident_like()?);
6732 match self.peek() {
6733 Token::Comma => {
6734 self.advance();
6735 }
6736 Token::RParen => {
6737 self.advance();
6738 break;
6739 }
6740 other => {
6741 return Err(self.err(alloc::format!(
6742 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
6743 )));
6744 }
6745 }
6746 }
6747 }
6748 match self.advance() {
6750 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
6751 other => {
6752 return Err(self.err(alloc::format!(
6753 "expected DO after ON CONFLICT [(…)], got {other:?}"
6754 )));
6755 }
6756 }
6757 let action = match self.advance() {
6759 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
6760 crate::ast::OnConflictAction::Nothing
6761 }
6762 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
6763 self.parse_on_conflict_update_action()?
6764 }
6765 other => {
6766 return Err(self.err(alloc::format!(
6767 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
6768 )));
6769 }
6770 };
6771 Ok(Some(crate::ast::OnConflictClause {
6772 target_columns,
6773 action,
6774 }))
6775 }
6776
6777 fn parse_on_conflict_update_action(
6781 &mut self,
6782 ) -> Result<crate::ast::OnConflictAction, ParseError> {
6783 match self.advance() {
6785 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
6786 other => {
6787 return Err(self.err(alloc::format!(
6788 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
6789 )));
6790 }
6791 }
6792 let mut assignments: Vec<(String, Expr)> = Vec::new();
6793 loop {
6794 let col = self.expect_ident_like()?;
6795 if !matches!(self.peek(), Token::Eq) {
6796 return Err(self.err(alloc::format!(
6797 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
6798 self.peek()
6799 )));
6800 }
6801 self.advance();
6802 let value = self.parse_expr(0)?;
6803 assignments.push((col, value));
6804 if matches!(self.peek(), Token::Comma) {
6805 self.advance();
6806 continue;
6807 }
6808 break;
6809 }
6810 let where_ = if matches!(self.peek(), Token::Where) {
6811 self.advance();
6812 Some(self.parse_expr(0)?)
6813 } else {
6814 None
6815 };
6816 Ok(crate::ast::OnConflictAction::Update {
6817 assignments,
6818 where_,
6819 })
6820 }
6821
6822 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
6823 let mut items = Vec::new();
6824 loop {
6825 items.push(self.parse_select_item()?);
6826 if matches!(self.peek(), Token::Comma) {
6827 self.advance();
6828 } else {
6829 break;
6830 }
6831 }
6832 Ok(items)
6833 }
6834
6835 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
6836 if matches!(self.peek(), Token::Star) {
6837 self.advance();
6838 return Ok(SelectItem::Wildcard);
6839 }
6840 let expr = self.parse_expr(0)?;
6841 let alias = self.parse_optional_alias();
6842 Ok(SelectItem::Expr { expr, alias })
6843 }
6844
6845 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
6846 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
6852 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6853 {
6854 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
6858 Statement::Select(s) => s,
6859 other => {
6860 return Err(self.err(alloc::format!(
6861 "expected SELECT inside LATERAL ( … ), got {other:?}"
6862 )));
6863 }
6864 };
6865 if !matches!(self.peek(), Token::RParen) {
6866 return Err(self.err(alloc::format!(
6867 "expected ')' after LATERAL subquery, got {:?}",
6868 self.peek()
6869 )));
6870 }
6871 self.advance();
6872 let alias_ident = self.parse_optional_alias();
6873 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
6874 return Ok(TableRef {
6875 name,
6876 alias: alias_ident,
6877 as_of_segment: None,
6878 unnest_expr: None,
6879 unnest_column_aliases: Vec::new(),
6880 generate_series_args: None,
6881 lateral_subquery: Some(Box::new(inner)),
6882 });
6883 }
6884 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
6888 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6889 {
6890 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
6893 if !matches!(self.peek(), Token::RParen) {
6894 return Err(self.err(alloc::format!(
6895 "expected ')' after unnest() argument, got {:?}",
6896 self.peek()
6897 )));
6898 }
6899 self.advance();
6900 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
6901 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
6902 return Ok(TableRef {
6903 name,
6904 alias: alias_ident,
6905 as_of_segment: None,
6906 unnest_expr: Some(Box::new(expr)),
6907 unnest_column_aliases,
6908 generate_series_args: None,
6909 lateral_subquery: None,
6910 });
6911 }
6912 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
6922 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
6923 {
6924 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
6927 loop {
6928 args.push(self.parse_expr(0)?);
6929 if matches!(self.peek(), Token::Comma) {
6930 self.advance();
6931 continue;
6932 }
6933 break;
6934 }
6935 if !matches!(self.peek(), Token::RParen) {
6936 return Err(self.err(alloc::format!(
6937 "expected ')' after generate_series() arguments, got {:?}",
6938 self.peek()
6939 )));
6940 }
6941 self.advance();
6942 if args.len() < 2 || args.len() > 3 {
6943 return Err(self.err(alloc::format!(
6944 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
6945 args.len()
6946 )));
6947 }
6948 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
6949 let name = alias_ident
6950 .clone()
6951 .unwrap_or_else(|| "generate_series".to_string());
6952 return Ok(TableRef {
6953 name,
6954 alias: alias_ident,
6955 as_of_segment: None,
6956 unnest_expr: None,
6957 unnest_column_aliases: Vec::new(),
6958 generate_series_args: Some(args),
6959 lateral_subquery: None,
6960 });
6961 }
6962 let name = if let Some(synth) = self.try_peek_meta_qualified() {
6971 synth
6972 } else if let Some(synth) = self.try_peek_meta_bare() {
6973 synth
6974 } else {
6975 self.expect_ident_like()?
6976 };
6977 let as_of_segment = if matches!(self.peek(), Token::As)
6983 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
6984 {
6985 self.advance(); self.advance(); let kw = match self.peek().clone() {
6988 Token::Ident(s) | Token::QuotedIdent(s) => s,
6989 other => {
6990 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
6991 }
6992 };
6993 if !kw.eq_ignore_ascii_case("segment") {
6994 return Err(self.err(format!(
6995 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
6996 )));
6997 }
6998 self.advance();
6999 let id = match self.advance() {
7002 Token::String(s) => s
7003 .parse::<u32>()
7004 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7005 Token::Integer(n) => u32::try_from(n)
7006 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7007 other => {
7008 return Err(self.err(format!(
7009 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7010 )));
7011 }
7012 };
7013 Some(id)
7014 } else {
7015 None
7016 };
7017 let alias = self.parse_optional_alias();
7018 Ok(TableRef {
7019 name,
7020 alias,
7021 as_of_segment,
7022 unnest_expr: None,
7023 unnest_column_aliases: Vec::new(),
7024 generate_series_args: None,
7025 lateral_subquery: None,
7026 })
7027 }
7028
7029 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
7035 let alias = self.parse_optional_alias();
7036 if alias.is_none() {
7037 return (None, Vec::new());
7038 }
7039 let mut cols: Vec<String> = Vec::new();
7040 if matches!(self.peek(), Token::LParen) {
7041 self.advance();
7042 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7043 self.advance();
7044 cols.push(s);
7045 if matches!(self.peek(), Token::Comma) {
7046 self.advance();
7047 continue;
7048 }
7049 break;
7050 }
7051 if matches!(self.peek(), Token::RParen) {
7052 self.advance();
7053 }
7054 }
7055 (alias, cols)
7056 }
7057
7058 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7063 let primary = self.parse_table_ref()?;
7064 let mut joins = Vec::new();
7065 loop {
7066 if matches!(self.peek(), Token::Comma) {
7068 self.advance();
7069 let table = self.parse_table_ref()?;
7070 joins.push(FromJoin {
7071 kind: JoinKind::Cross,
7072 table,
7073 on: None,
7074 });
7075 continue;
7076 }
7077 let kind =
7080 match self.peek() {
7081 Token::Inner => {
7082 self.advance();
7083 if !matches!(self.peek(), Token::Join) {
7084 return Err(self
7085 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7086 }
7087 self.advance();
7088 JoinKind::Inner
7089 }
7090 Token::Left => {
7091 self.advance();
7092 if matches!(self.peek(), Token::Outer) {
7093 self.advance();
7094 }
7095 if !matches!(self.peek(), Token::Join) {
7096 return Err(self.err(format!(
7097 "expected JOIN after LEFT [OUTER], got {:?}",
7098 self.peek()
7099 )));
7100 }
7101 self.advance();
7102 JoinKind::Left
7103 }
7104 Token::Cross => {
7105 self.advance();
7106 if !matches!(self.peek(), Token::Join) {
7107 return Err(self
7108 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7109 }
7110 self.advance();
7111 JoinKind::Cross
7112 }
7113 Token::Join => {
7114 self.advance();
7115 JoinKind::Inner
7116 }
7117 _ => break,
7118 };
7119 let table = self.parse_table_ref()?;
7120 let on = if matches!(self.peek(), Token::On) {
7121 self.advance();
7122 Some(self.parse_expr(0)?)
7123 } else if kind == JoinKind::Cross {
7124 None
7125 } else {
7126 return Err(self.err(format!(
7127 "expected ON after {:?} JOIN, got {:?}",
7128 kind,
7129 self.peek()
7130 )));
7131 };
7132 joins.push(FromJoin { kind, table, on });
7133 }
7134 Ok(FromClause { primary, joins })
7135 }
7136
7137 fn parse_optional_alias(&mut self) -> Option<String> {
7142 if matches!(self.peek(), Token::As) {
7143 self.advance();
7144 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7149 return self.expect_ident_like().ok();
7150 }
7151 return None;
7152 }
7153 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7161 if is_alias_stopword(s) {
7162 return None;
7163 }
7164 return self.expect_ident_like().ok();
7165 }
7166 None
7167 }
7168
7169 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7171 let mut lhs = self.parse_unary()?;
7172 while let Some((op, prec)) = binop_from(self.peek()) {
7173 if prec < min_prec {
7174 break;
7175 }
7176 self.advance();
7177 let any_kind = match self.peek() {
7182 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7183 Some(false)
7184 }
7185 Token::Ident(s) | Token::QuotedIdent(s)
7186 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7187 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7188 {
7189 Some(s.eq_ignore_ascii_case("any"))
7190 }
7191 _ => None,
7192 };
7193 if let Some(is_any) = any_kind {
7194 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7197 if !matches!(self.peek(), Token::RParen) {
7198 return Err(self.err(alloc::format!(
7199 "expected ')' after ANY/ALL argument, got {:?}",
7200 self.peek()
7201 )));
7202 }
7203 self.advance();
7204 lhs = Expr::AnyAll {
7205 expr: Box::new(lhs),
7206 op,
7207 array: Box::new(arr),
7208 is_any,
7209 };
7210 continue;
7211 }
7212 let rhs = self.parse_expr(prec + 1)?;
7213 lhs = Expr::Binary {
7214 lhs: Box::new(lhs),
7215 op,
7216 rhs: Box::new(rhs),
7217 };
7218 }
7219 Ok(lhs)
7220 }
7221
7222 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7223 match self.peek() {
7224 Token::Not => {
7225 self.advance();
7226 let e = self.parse_expr(3)?;
7229 Ok(Expr::Unary {
7230 op: UnOp::Not,
7231 expr: Box::new(e),
7232 })
7233 }
7234 Token::Minus => {
7235 self.advance();
7236 let e = self.parse_expr(8)?;
7239 Ok(Expr::Unary {
7240 op: UnOp::Neg,
7241 expr: Box::new(e),
7242 })
7243 }
7244 Token::Tilde => {
7245 self.advance();
7246 let e = self.parse_expr(8)?;
7248 Ok(Expr::Unary {
7249 op: UnOp::BitNot,
7250 expr: Box::new(e),
7251 })
7252 }
7253 _ => self.parse_atom(),
7254 }
7255 }
7256
7257 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7258 let tok_pos = self.pos;
7259 match self.advance() {
7260 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7261 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7262 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7263 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7264 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7265 Token::Null => Ok(Expr::Literal(Literal::Null)),
7266 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7270 Token::LParen => {
7271 if matches!(self.peek(), Token::Select) {
7275 let inner = self.parse_select_stmt()?;
7276 match self.advance() {
7277 Token::RParen => {
7278 let Statement::Select(s) = inner else {
7279 unreachable!("parse_select_stmt returns Select")
7280 };
7281 Ok(Expr::ScalarSubquery(Box::new(s)))
7282 }
7283 other => Err(ParseError {
7284 message: format!("expected ')' after scalar subquery, got {other:?}"),
7285 token_pos: self.pos.saturating_sub(1),
7286 }),
7287 }
7288 } else {
7289 let e = self.parse_expr(0)?;
7290 match self.advance() {
7291 Token::RParen => Ok(e),
7292 other => Err(ParseError {
7293 message: format!("expected ')', got {other:?}"),
7294 token_pos: self.pos.saturating_sub(1),
7295 }),
7296 }
7297 }
7298 }
7299 Token::LBracket => self.parse_vector_literal_body(),
7300 Token::Extract => self.parse_extract_atom(),
7301 Token::Interval => self.parse_interval_atom(),
7302 Token::Left if matches!(self.peek(), Token::LParen) => {
7309 self.advance(); let mut args = Vec::new();
7311 if !matches!(self.peek(), Token::RParen) {
7312 loop {
7313 args.push(self.parse_expr(0)?);
7314 match self.peek() {
7315 Token::Comma => {
7316 self.advance();
7317 }
7318 Token::RParen => break,
7319 other => {
7320 return Err(self.err(alloc::format!(
7321 "expected ',' or ')' in left() args, got {other:?}"
7322 )));
7323 }
7324 }
7325 }
7326 }
7327 self.advance(); Ok(Expr::FunctionCall {
7329 name: "left".into(),
7330 args,
7331 })
7332 }
7333 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7338 self.parse_exists_atom(false)
7339 }
7340 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7344 self.parse_case_atom()
7345 }
7346 Token::Ident(s) | Token::QuotedIdent(s)
7350 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7351 {
7352 self.advance(); let mut items: Vec<Expr> = Vec::new();
7354 if !matches!(self.peek(), Token::RBracket) {
7355 loop {
7356 items.push(self.parse_expr(0)?);
7357 match self.peek() {
7358 Token::Comma => {
7359 self.advance();
7360 }
7361 Token::RBracket => break,
7362 other => {
7363 return Err(self.err(alloc::format!(
7364 "expected ',' or ']' in ARRAY literal, got {other:?}"
7365 )));
7366 }
7367 }
7368 }
7369 }
7370 self.advance(); Ok(Expr::Array(items))
7372 }
7373 Token::Ident(s) | Token::QuotedIdent(s)
7388 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7389 {
7390 self.parse_match_against_atom()
7391 }
7392 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7393 other => Err(ParseError {
7394 message: format!("unexpected token {other:?} in expression"),
7395 token_pos: tok_pos,
7396 }),
7397 }
7398 .and_then(|atom| self.finish_postfix_casts(atom))
7400 }
7401
7402 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7405 loop {
7406 if matches!(self.peek(), Token::DoubleColon) {
7407 self.advance();
7408 let target = match self.advance() {
7413 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7414 "int" | "integer" | "int4" => {
7415 if matches!(self.peek(), Token::LBracket)
7416 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7417 {
7418 self.advance();
7419 self.advance();
7420 CastTarget::IntArray
7421 } else {
7422 CastTarget::Int
7423 }
7424 }
7425 "bigint" | "int8" => {
7426 if matches!(self.peek(), Token::LBracket)
7427 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7428 {
7429 self.advance();
7430 self.advance();
7431 CastTarget::BigIntArray
7432 } else {
7433 CastTarget::BigInt
7434 }
7435 }
7436 "float" | "double" | "real" => CastTarget::Float,
7437 "text" => {
7438 if matches!(self.peek(), Token::LBracket)
7440 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7441 {
7442 self.advance();
7443 self.advance();
7444 CastTarget::TextArray
7445 } else {
7446 CastTarget::Text
7447 }
7448 }
7449 "bool" | "boolean" => CastTarget::Bool,
7450 "vector" => CastTarget::Vector,
7451 "date" => CastTarget::Date,
7452 "timestamp" | "datetime" => CastTarget::Timestamp,
7453 "timestamptz" => CastTarget::Timestamptz,
7454 "interval" => CastTarget::Interval,
7455 "json" => CastTarget::Json,
7456 "jsonb" => CastTarget::Jsonb,
7457 "regtype" => CastTarget::RegType,
7458 "regclass" => CastTarget::RegClass,
7459 "tsvector" => CastTarget::TsVector,
7463 "tsquery" => CastTarget::TsQuery,
7464 "uuid" => CastTarget::Uuid,
7467 "bytea" => CastTarget::Bytea,
7472 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7477 other => {
7478 return Err(ParseError {
7479 message: format!("unsupported cast target `::{other}`"),
7480 token_pos: self.pos.saturating_sub(1),
7481 });
7482 }
7483 },
7484 Token::Interval => CastTarget::Interval,
7485 other => {
7486 return Err(ParseError {
7487 message: format!("expected type ident after `::`, got {other:?}"),
7488 token_pos: self.pos.saturating_sub(1),
7489 });
7490 }
7491 };
7492 expr = Expr::Cast {
7493 expr: Box::new(expr),
7494 target,
7495 };
7496 continue;
7497 }
7498 if matches!(self.peek(), Token::Is) {
7499 self.advance();
7500 let negated = if matches!(self.peek(), Token::Not) {
7501 self.advance();
7502 true
7503 } else {
7504 false
7505 };
7506 if matches!(self.peek(), Token::Distinct) {
7509 self.advance();
7510 if !matches!(self.peek(), Token::From) {
7511 return Err(self.err(format!(
7512 "expected FROM after IS{} DISTINCT, got {:?}",
7513 if negated { " NOT" } else { "" },
7514 self.peek()
7515 )));
7516 }
7517 self.advance();
7518 let rhs = self.parse_expr(20)?;
7522 let op = if negated {
7523 BinOp::IsNotDistinctFrom
7524 } else {
7525 BinOp::IsDistinctFrom
7526 };
7527 expr = Expr::Binary {
7528 op,
7529 lhs: Box::new(expr),
7530 rhs: Box::new(rhs),
7531 };
7532 continue;
7533 }
7534 if !matches!(self.peek(), Token::Null) {
7535 return Err(self.err(format!(
7536 "expected NULL or DISTINCT after IS{}, got {:?}",
7537 if negated { " NOT" } else { "" },
7538 self.peek()
7539 )));
7540 }
7541 self.advance();
7542 expr = Expr::IsNull {
7543 expr: Box::new(expr),
7544 negated,
7545 };
7546 continue;
7547 }
7548 let negated = if matches!(self.peek(), Token::Not) {
7552 let next = self.tokens.get(self.pos + 1);
7553 matches!(next, Some(Token::Between | Token::In | Token::Like))
7554 } else {
7555 false
7556 };
7557 if negated {
7558 self.advance();
7559 }
7560 if matches!(self.peek(), Token::Between) {
7561 expr = self.parse_between_tail(expr, negated)?;
7562 continue;
7563 }
7564 if matches!(self.peek(), Token::In) {
7565 expr = self.parse_in_tail(expr, negated)?;
7566 continue;
7567 }
7568 if matches!(self.peek(), Token::Like) {
7569 self.advance();
7570 let pattern = self.parse_expr(5)?;
7573 expr = Expr::Like {
7574 expr: Box::new(expr),
7575 pattern: Box::new(pattern),
7576 negated,
7577 };
7578 continue;
7579 }
7580 if matches!(self.peek(), Token::LBracket) {
7584 self.advance();
7585 let index = self.parse_expr(0)?;
7586 if !matches!(self.peek(), Token::RBracket) {
7587 return Err(self.err(alloc::format!(
7588 "expected ']' after array index, got {:?}",
7589 self.peek()
7590 )));
7591 }
7592 self.advance();
7593 expr = Expr::ArraySubscript {
7594 target: Box::new(expr),
7595 index: Box::new(index),
7596 };
7597 continue;
7598 }
7599 return Ok(expr);
7600 }
7601 }
7602
7603 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7607 self.advance(); let low = self.parse_expr(5)?;
7609 if !matches!(self.peek(), Token::And) {
7610 return Err(self.err(format!(
7611 "expected AND after BETWEEN low bound, got {:?}",
7612 self.peek()
7613 )));
7614 }
7615 self.advance();
7616 let high = self.parse_expr(5)?;
7617 let target = Box::new(expr);
7618 let combined = Expr::Binary {
7619 lhs: Box::new(Expr::Binary {
7620 lhs: target.clone(),
7621 op: BinOp::GtEq,
7622 rhs: Box::new(low),
7623 }),
7624 op: BinOp::And,
7625 rhs: Box::new(Expr::Binary {
7626 lhs: target,
7627 op: BinOp::LtEq,
7628 rhs: Box::new(high),
7629 }),
7630 };
7631 Ok(maybe_not(combined, negated))
7632 }
7633
7634 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
7639 let mut recursive = false;
7644 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
7645 && s.eq_ignore_ascii_case("recursive")
7646 {
7647 self.advance();
7648 recursive = true;
7649 }
7650 let mut ctes = Vec::new();
7651 loop {
7652 let name = self.expect_ident_like()?;
7653 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
7657 self.advance();
7658 let mut names = Vec::new();
7659 loop {
7660 names.push(self.expect_ident_like()?);
7661 if matches!(self.peek(), Token::Comma) {
7662 self.advance();
7663 continue;
7664 }
7665 break;
7666 }
7667 if !matches!(self.peek(), Token::RParen) {
7668 return Err(self.err(format!(
7669 "expected ')' to close CTE column list, got {:?}",
7670 self.peek()
7671 )));
7672 }
7673 self.advance();
7674 names
7675 } else {
7676 Vec::new()
7677 };
7678 if !matches!(self.peek(), Token::As) {
7682 return Err(self.err(format!(
7683 "expected AS after CTE name {name:?}, got {:?}",
7684 self.peek()
7685 )));
7686 }
7687 self.advance();
7688 if !matches!(self.peek(), Token::LParen) {
7689 return Err(self.err(format!(
7690 "expected '(' after AS in WITH clause, got {:?}",
7691 self.peek()
7692 )));
7693 }
7694 self.advance();
7695 if !matches!(self.peek(), Token::Select) {
7696 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
7697 }
7698 let inner = self.parse_select_stmt()?;
7699 if !matches!(self.peek(), Token::RParen) {
7700 return Err(self.err(format!(
7701 "expected ')' after CTE body, got {:?}",
7702 self.peek()
7703 )));
7704 }
7705 self.advance();
7706 let Statement::Select(body) = inner else {
7707 unreachable!("parse_select_stmt returns Select")
7708 };
7709 ctes.push(crate::ast::Cte {
7710 name,
7711 body,
7712 recursive,
7713 column_overrides,
7714 });
7715 if matches!(self.peek(), Token::Comma) {
7716 self.advance();
7717 continue;
7718 }
7719 break;
7720 }
7721 if !matches!(self.peek(), Token::Select) {
7723 return Err(self.err(format!(
7724 "expected SELECT after WITH clause, got {:?}",
7725 self.peek()
7726 )));
7727 }
7728 let body_stmt = self.parse_select_stmt()?;
7729 let Statement::Select(mut body) = body_stmt else {
7730 unreachable!()
7731 };
7732 body.ctes = ctes;
7733 Ok(Statement::Select(body))
7734 }
7735
7736 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
7745 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
7749 None
7750 } else {
7751 Some(Box::new(self.parse_expr(0)?))
7752 };
7753 let mut branches: Vec<(Expr, Expr)> = Vec::new();
7754 loop {
7755 match self.peek() {
7756 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
7757 self.advance();
7758 let cond = self.parse_expr(0)?;
7759 match self.peek() {
7760 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
7761 self.advance();
7762 }
7763 other => {
7764 return Err(self.err(alloc::format!(
7765 "expected THEN after CASE WHEN <expr>, got {other:?}"
7766 )));
7767 }
7768 }
7769 let value = self.parse_expr(0)?;
7770 branches.push((cond, value));
7771 }
7772 _ => break,
7773 }
7774 }
7775 if branches.is_empty() {
7776 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
7777 }
7778 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
7779 {
7780 self.advance();
7781 Some(Box::new(self.parse_expr(0)?))
7782 } else {
7783 None
7784 };
7785 match self.peek() {
7786 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
7787 self.advance();
7788 }
7789 other => {
7790 return Err(self.err(alloc::format!(
7791 "expected END to close CASE expression, got {other:?}"
7792 )));
7793 }
7794 }
7795 Ok(Expr::Case {
7796 operand,
7797 branches,
7798 else_branch,
7799 })
7800 }
7801
7802 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
7803 if !matches!(self.peek(), Token::LParen) {
7804 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
7805 }
7806 self.advance();
7807 let inner = self.parse_select_stmt()?;
7808 if !matches!(self.peek(), Token::RParen) {
7809 return Err(self.err(format!(
7810 "expected ')' after EXISTS-subquery, got {:?}",
7811 self.peek()
7812 )));
7813 }
7814 self.advance();
7815 let Statement::Select(s) = inner else {
7816 unreachable!("parse_select_stmt returns Select")
7817 };
7818 Ok(Expr::Exists {
7819 subquery: Box::new(s),
7820 negated,
7821 })
7822 }
7823
7824 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7825 self.advance(); if !matches!(self.peek(), Token::LParen) {
7827 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
7828 }
7829 self.advance();
7830 if matches!(self.peek(), Token::Select) {
7832 let inner = self.parse_select_stmt()?;
7833 if !matches!(self.peek(), Token::RParen) {
7834 return Err(self.err(format!(
7835 "expected ')' after IN-subquery, got {:?}",
7836 self.peek()
7837 )));
7838 }
7839 self.advance();
7840 let Statement::Select(s) = inner else {
7841 unreachable!("parse_select_stmt always returns Statement::Select")
7842 };
7843 return Ok(Expr::InSubquery {
7844 expr: Box::new(expr),
7845 subquery: Box::new(s),
7846 negated,
7847 });
7848 }
7849 let mut elements = Vec::new();
7850 if !matches!(self.peek(), Token::RParen) {
7851 loop {
7852 elements.push(self.parse_expr(0)?);
7853 match self.peek() {
7854 Token::Comma => {
7855 self.advance();
7856 }
7857 Token::RParen => break,
7858 other => {
7859 return Err(
7860 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
7861 );
7862 }
7863 }
7864 }
7865 }
7866 self.advance(); let target = Box::new(expr);
7868 let combined = if elements.is_empty() {
7869 Expr::Literal(Literal::Bool(false))
7870 } else {
7871 let mut iter = elements.into_iter();
7872 let first = iter.next().unwrap();
7873 let mut acc = Expr::Binary {
7874 lhs: target.clone(),
7875 op: BinOp::Eq,
7876 rhs: Box::new(first),
7877 };
7878 for elt in iter {
7879 acc = Expr::Binary {
7880 lhs: Box::new(acc),
7881 op: BinOp::Or,
7882 rhs: Box::new(Expr::Binary {
7883 lhs: target.clone(),
7884 op: BinOp::Eq,
7885 rhs: Box::new(elt),
7886 }),
7887 };
7888 }
7889 acc
7890 };
7891 Ok(maybe_not(combined, negated))
7892 }
7893
7894 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
7915 if !matches!(self.peek(), Token::LParen) {
7918 return Err(self.err(alloc::format!(
7919 "expected '(' after MATCH, got {:?}",
7920 self.peek()
7921 )));
7922 }
7923 self.advance();
7924 let mut cols: Vec<Expr> = Vec::new();
7925 loop {
7926 cols.push(self.parse_expr(0)?);
7927 match self.peek() {
7928 Token::Comma => {
7929 self.advance();
7930 }
7931 Token::RParen => break,
7932 other => {
7933 return Err(self.err(alloc::format!(
7934 "expected ',' or ')' in MATCH column list, got {other:?}"
7935 )));
7936 }
7937 }
7938 }
7939 self.advance(); match self.peek() {
7942 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
7943 self.advance();
7944 }
7945 other => {
7946 return Err(self.err(alloc::format!(
7947 "expected AGAINST after MATCH column list, got {other:?}"
7948 )));
7949 }
7950 }
7951 if !matches!(self.peek(), Token::LParen) {
7952 return Err(self.err(alloc::format!(
7953 "expected '(' after AGAINST, got {:?}",
7954 self.peek()
7955 )));
7956 }
7957 self.advance();
7958 let term = match self.advance() {
7968 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
7969 Token::Placeholder(n) => Expr::Placeholder(n),
7970 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
7971 qualifier: None,
7972 name: s,
7973 }),
7974 other => {
7975 return Err(self.err(alloc::format!(
7976 "MATCH ... AGAINST(<term>) expects a string literal, \
7977 bound parameter, or column ref, got {other:?}"
7978 )));
7979 }
7980 };
7981 loop {
7986 match self.peek() {
7987 Token::In => {
7990 self.advance();
7991 }
7992 Token::Ident(s) | Token::QuotedIdent(s)
7993 if s.eq_ignore_ascii_case("natural")
7994 || s.eq_ignore_ascii_case("language")
7995 || s.eq_ignore_ascii_case("boolean")
7996 || s.eq_ignore_ascii_case("mode")
7997 || s.eq_ignore_ascii_case("with")
7998 || s.eq_ignore_ascii_case("query")
7999 || s.eq_ignore_ascii_case("expansion") =>
8000 {
8001 self.advance();
8002 }
8003 _ => break,
8004 }
8005 }
8006 if !matches!(self.peek(), Token::RParen) {
8007 return Err(self.err(alloc::format!(
8008 "expected ')' to close AGAINST, got {:?}",
8009 self.peek()
8010 )));
8011 }
8012 self.advance();
8013 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
8016 let plainto = Expr::FunctionCall {
8017 name: String::from("plainto_tsquery"),
8018 args: alloc::vec![simple_lit(), term.clone()],
8019 };
8020 let mut folded: Option<Expr> = None;
8021 for col in cols {
8022 let to_tsv = Expr::FunctionCall {
8023 name: String::from("to_tsvector"),
8024 args: alloc::vec![simple_lit(), col],
8025 };
8026 let leaf = Expr::Binary {
8027 lhs: Box::new(to_tsv),
8028 op: crate::ast::BinOp::TsMatch,
8029 rhs: Box::new(plainto.clone()),
8030 };
8031 folded = Some(match folded {
8032 None => leaf,
8033 Some(prev) => Expr::Binary {
8034 lhs: Box::new(prev),
8035 op: crate::ast::BinOp::Or,
8036 rhs: Box::new(leaf),
8037 },
8038 });
8039 }
8040 match folded {
8041 Some(e) => Ok(e),
8042 None => Err(self.err(String::from(
8043 "MATCH(...) AGAINST(...) requires at least one column",
8044 ))),
8045 }
8046 }
8047
8048 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
8049 if !matches!(self.peek(), Token::LParen) {
8050 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
8051 }
8052 self.advance();
8053 let field_name = self.expect_ident_like()?;
8054 let field = match field_name.to_ascii_lowercase().as_str() {
8055 "year" => ExtractField::Year,
8056 "month" => ExtractField::Month,
8057 "day" => ExtractField::Day,
8058 "hour" => ExtractField::Hour,
8059 "minute" => ExtractField::Minute,
8060 "second" => ExtractField::Second,
8061 "microsecond" | "microseconds" => ExtractField::Microsecond,
8062 "epoch" => ExtractField::Epoch,
8063 other => {
8064 return Err(self.err(format!(
8065 "unknown EXTRACT field {other:?}; \
8066 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
8067 )));
8068 }
8069 };
8070 if !matches!(self.peek(), Token::From) {
8071 return Err(self.err(format!(
8072 "expected FROM after EXTRACT field, got {:?}",
8073 self.peek()
8074 )));
8075 }
8076 self.advance();
8077 let source = self.parse_expr(0)?;
8078 if !matches!(self.peek(), Token::RParen) {
8079 return Err(self.err(format!(
8080 "expected ')' to close EXTRACT, got {:?}",
8081 self.peek()
8082 )));
8083 }
8084 self.advance();
8085 Ok(Expr::Extract {
8086 field,
8087 source: Box::new(source),
8088 })
8089 }
8090
8091 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8096 let tok = self.advance();
8097 let Token::String(text) = tok else {
8098 return Err(self.err(format!(
8099 "expected string literal after INTERVAL, got {tok:?}"
8100 )));
8101 };
8102 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8103 message: format!(
8104 "cannot parse INTERVAL {text:?}; \
8105 expected `<n> <unit> [<n> <unit> ...]` with units \
8106 microsecond[s], millisecond[s], second[s], minute[s], \
8107 hour[s], day[s], week[s], month[s], year[s]"
8108 ),
8109 token_pos: self.pos.saturating_sub(1),
8110 })?;
8111 Ok(Expr::Literal(Literal::Interval {
8112 months,
8113 micros,
8114 text,
8115 }))
8116 }
8117
8118 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8119 let mut elems = Vec::new();
8120 if matches!(self.peek(), Token::RBracket) {
8121 self.advance();
8122 return Ok(Expr::Literal(Literal::Vector(elems)));
8123 }
8124 loop {
8125 let e = self.parse_expr(0)?;
8126 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8127 message: format!("vector element must be a numeric literal, got {e:?}"),
8128 token_pos: self.pos,
8129 })?;
8130 elems.push(x);
8131 match self.peek() {
8132 Token::Comma => {
8133 self.advance();
8134 }
8135 Token::RBracket => {
8136 self.advance();
8137 break;
8138 }
8139 other => {
8140 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8141 }
8142 }
8143 }
8144 Ok(Expr::Literal(Literal::Vector(elems)))
8145 }
8146
8147 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8156 let Token::Ident(s) = self.peek().clone() else {
8157 return NullTreatment::Respect;
8158 };
8159 let is_ignore = s.eq_ignore_ascii_case("ignore");
8160 let is_respect = s.eq_ignore_ascii_case("respect");
8161 if !is_ignore && !is_respect {
8162 return NullTreatment::Respect;
8163 }
8164 if self.pos + 1 < self.tokens.len()
8167 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8168 && s2.eq_ignore_ascii_case("nulls")
8169 {
8170 self.advance();
8171 self.advance();
8172 return if is_ignore {
8173 NullTreatment::Ignore
8174 } else {
8175 NullTreatment::Respect
8176 };
8177 }
8178 NullTreatment::Respect
8179 }
8180
8181 #[allow(clippy::type_complexity)] fn parse_over_clause(
8184 &mut self,
8185 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
8186 if !matches!(self.peek(), Token::LParen) {
8187 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8188 }
8189 self.advance();
8190 let mut partition_by = Vec::new();
8191 let mut order_by = Vec::new();
8192 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8194 && s.eq_ignore_ascii_case("partition")
8195 {
8196 self.advance();
8197 if !matches!(self.peek(), Token::By) {
8198 return Err(self.err(format!(
8199 "expected BY after PARTITION, got {:?}",
8200 self.peek()
8201 )));
8202 }
8203 self.advance();
8204 loop {
8205 partition_by.push(self.parse_expr(0)?);
8206 if matches!(self.peek(), Token::Comma) {
8207 self.advance();
8208 continue;
8209 }
8210 break;
8211 }
8212 }
8213 if matches!(self.peek(), Token::Order) {
8215 self.advance();
8216 if !matches!(self.peek(), Token::By) {
8217 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8218 }
8219 self.advance();
8220 loop {
8221 let e = self.parse_expr(0)?;
8222 let desc = if matches!(self.peek(), Token::Desc) {
8223 self.advance();
8224 true
8225 } else if matches!(self.peek(), Token::Asc) {
8226 self.advance();
8227 false
8228 } else {
8229 false
8230 };
8231 order_by.push((e, desc));
8232 if matches!(self.peek(), Token::Comma) {
8233 self.advance();
8234 continue;
8235 }
8236 break;
8237 }
8238 }
8239 let mut frame: Option<WindowFrame> = None;
8243 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8244 let kind = if s.eq_ignore_ascii_case("rows") {
8245 Some(FrameKind::Rows)
8246 } else if s.eq_ignore_ascii_case("range") {
8247 Some(FrameKind::Range)
8248 } else {
8249 None
8250 };
8251 if let Some(kind) = kind {
8252 self.advance();
8253 frame = Some(self.parse_frame_tail(kind)?);
8254 }
8255 }
8256 if !matches!(self.peek(), Token::RParen) {
8257 return Err(self.err(format!(
8258 "expected ')' to close OVER clause, got {:?}",
8259 self.peek()
8260 )));
8261 }
8262 self.advance();
8263 Ok((partition_by, order_by, frame))
8264 }
8265
8266 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8272 if matches!(self.peek(), Token::Between) {
8273 self.advance();
8274 let start = self.parse_frame_bound()?;
8275 if !matches!(self.peek(), Token::And) {
8276 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8277 }
8278 self.advance();
8279 let end = self.parse_frame_bound()?;
8280 Ok(WindowFrame {
8281 kind,
8282 start,
8283 end: Some(end),
8284 })
8285 } else {
8286 let start = self.parse_frame_bound()?;
8287 Ok(WindowFrame {
8288 kind,
8289 start,
8290 end: None,
8291 })
8292 }
8293 }
8294
8295 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8298 if let Token::Integer(n) = *self.peek() {
8300 self.advance();
8301 let n: u64 = u64::try_from(n).map_err(|_| {
8302 self.err(format!(
8303 "invalid frame offset {n} — expected non-negative integer"
8304 ))
8305 })?;
8306 let dir = self.expect_ident_like()?;
8307 return if dir.eq_ignore_ascii_case("preceding") {
8308 Ok(FrameBound::OffsetPreceding(n))
8309 } else if dir.eq_ignore_ascii_case("following") {
8310 Ok(FrameBound::OffsetFollowing(n))
8311 } else {
8312 Err(self.err(format!(
8313 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8314 )))
8315 };
8316 }
8317 let first = self.expect_ident_like()?;
8318 if first.eq_ignore_ascii_case("unbounded") {
8319 let dir = self.expect_ident_like()?;
8320 return if dir.eq_ignore_ascii_case("preceding") {
8321 Ok(FrameBound::UnboundedPreceding)
8322 } else if dir.eq_ignore_ascii_case("following") {
8323 Ok(FrameBound::UnboundedFollowing)
8324 } else {
8325 Err(self.err(format!(
8326 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8327 )))
8328 };
8329 }
8330 if first.eq_ignore_ascii_case("current") {
8331 let row = self.expect_ident_like()?;
8332 if !row.eq_ignore_ascii_case("row") {
8333 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8334 }
8335 return Ok(FrameBound::CurrentRow);
8336 }
8337 Err(self.err(format!(
8338 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8339 )))
8340 }
8341
8342 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8343 if matches!(self.peek(), Token::Dot) {
8344 self.advance();
8345 let name = self.expect_ident_like()?;
8346 if matches!(self.peek(), Token::LParen) {
8352 return self.finish_ident_atom(name);
8353 }
8354 return Ok(Expr::Column(ColumnName {
8355 qualifier: Some(first),
8356 name,
8357 }));
8358 }
8359 if matches!(self.peek(), Token::LParen) {
8360 self.advance();
8361 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8365 self.advance();
8366 if !matches!(self.peek(), Token::RParen) {
8367 return Err(self.err(format!(
8368 "expected ')' after COUNT(*), got {:?}",
8369 self.peek()
8370 )));
8371 }
8372 self.advance();
8373 let null_treatment = self.parse_null_treatment_modifier();
8375 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8376 && s.eq_ignore_ascii_case("over")
8377 {
8378 self.advance();
8379 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8380 return Ok(Expr::WindowFunction {
8381 name: "count_star".into(),
8382 args: Vec::new(),
8383 partition_by,
8384 order_by,
8385 frame,
8386 null_treatment,
8387 });
8388 }
8389 return Ok(Expr::FunctionCall {
8390 name: "count_star".into(),
8391 args: Vec::new(),
8392 });
8393 }
8394 let mut args = Vec::new();
8396 if !matches!(self.peek(), Token::RParen) {
8397 loop {
8398 args.push(self.parse_expr(0)?);
8399 match self.peek() {
8400 Token::Comma => {
8401 self.advance();
8402 }
8403 Token::RParen => break,
8404 other => {
8405 return Err(self.err(format!(
8406 "expected ',' or ')' in function args, got {other:?}"
8407 )));
8408 }
8409 }
8410 }
8411 }
8412 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8420 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8421 && s.eq_ignore_ascii_case("over")
8422 {
8423 self.advance();
8424 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8425 return Ok(Expr::WindowFunction {
8426 name: first,
8427 args,
8428 partition_by,
8429 order_by,
8430 frame,
8431 null_treatment,
8432 });
8433 }
8434 return Ok(Expr::FunctionCall { name: first, args });
8435 }
8436 let lc = first.to_ascii_lowercase();
8442 if matches!(
8443 lc.as_str(),
8444 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8445 ) {
8446 return Ok(Expr::FunctionCall {
8447 name: lc,
8448 args: Vec::new(),
8449 });
8450 }
8451 Ok(Expr::Column(ColumnName {
8452 qualifier: None,
8453 name: first,
8454 }))
8455 }
8456}
8457
8458fn extract_first_column(expr: &Expr) -> Option<String> {
8466 match expr {
8467 Expr::Column(cn) => Some(cn.name.clone()),
8468 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8469 Expr::Binary { lhs, rhs, .. } => {
8470 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8471 }
8472 Expr::Unary { expr: e, .. } => extract_first_column(e),
8473 _ => None,
8474 }
8475}
8476
8477fn maybe_not(expr: Expr, negated: bool) -> Expr {
8478 if negated {
8479 Expr::Unary {
8480 op: UnOp::Not,
8481 expr: Box::new(expr),
8482 }
8483 } else {
8484 expr
8485 }
8486}
8487
8488fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8489 let pair = match tok {
8490 Token::Or => (BinOp::Or, 1),
8491 Token::And => (BinOp::And, 2),
8492 Token::Eq => (BinOp::Eq, 4),
8493 Token::NotEq => (BinOp::NotEq, 4),
8494 Token::Lt => (BinOp::Lt, 4),
8495 Token::LtEq => (BinOp::LtEq, 4),
8496 Token::Gt => (BinOp::Gt, 4),
8497 Token::GtEq => (BinOp::GtEq, 4),
8498 Token::L2Distance => (BinOp::L2Distance, 5),
8501 Token::InnerProduct => (BinOp::InnerProduct, 5),
8502 Token::CosineDistance => (BinOp::CosineDistance, 5),
8503 Token::Plus => (BinOp::Add, 6),
8504 Token::Minus => (BinOp::Sub, 6),
8505 Token::Concat => (BinOp::Concat, 6),
8508 Token::Pipe => (BinOp::BitOr, 6),
8521 Token::Amp => (BinOp::BitAnd, 6),
8522 Token::Star => (BinOp::Mul, 7),
8523 Token::Slash => (BinOp::Div, 7),
8524 Token::JsonGet => (BinOp::JsonGet, 7),
8528 Token::JsonGetText => (BinOp::JsonGetText, 7),
8529 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
8530 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
8531 Token::JsonContains => (BinOp::JsonContains, 7),
8532 Token::TsMatch => (BinOp::TsMatch, 4),
8536 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
8540 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
8541 Token::InetContains => (BinOp::InetContains, 4),
8542 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
8543 Token::InetOverlap => (BinOp::InetOverlap, 4),
8544 _ => return None,
8545 };
8546 Some(pair)
8547}
8548
8549#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
8550fn is_alias_stopword(s: &str) -> bool {
8562 matches!(
8563 s.to_ascii_lowercase().as_str(),
8564 "with"
8565 | "on"
8566 | "where"
8567 | "having"
8568 | "group"
8569 | "order"
8570 | "limit"
8571 | "offset"
8572 | "union"
8573 | "except"
8574 | "intersect"
8575 | "returning"
8576 | "set"
8577 | "values"
8578 | "for"
8579 | "lateral"
8580 | "left"
8581 | "right"
8582 | "inner"
8583 | "outer"
8584 | "full"
8585 | "cross"
8586 | "join"
8587 | "natural"
8588 | "using"
8589 | "fetch"
8590 )
8591}
8592
8593fn extract_numeric_literal(e: &Expr) -> Option<f32> {
8594 match e {
8595 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
8596 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
8597 Expr::Unary {
8598 op: UnOp::Neg,
8599 expr,
8600 } => extract_numeric_literal(expr).map(|x| -x),
8601 _ => None,
8602 }
8603}
8604
8605pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
8613 let parts: Vec<&str> = s.split_whitespace().collect();
8614 if parts.is_empty() || !parts.len().is_multiple_of(2) {
8615 return None;
8616 }
8617 let mut months: i32 = 0;
8618 let mut micros: i64 = 0;
8619 let mut i = 0;
8620 while i < parts.len() {
8621 let n: i64 = parts[i].parse().ok()?;
8622 let unit = parts[i + 1].to_ascii_lowercase();
8623 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
8624 match unit_stripped {
8625 "microsecond" => micros = micros.checked_add(n)?,
8626 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
8627 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
8628 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
8629 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
8630 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
8631 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
8632 "month" => {
8633 let n32 = i32::try_from(n).ok()?;
8634 months = months.checked_add(n32)?;
8635 }
8636 "year" => {
8637 let n32 = i32::try_from(n).ok()?;
8638 months = months.checked_add(n32.checked_mul(12)?)?;
8639 }
8640 _ => return None,
8641 }
8642 i += 2;
8643 }
8644 Some((months, micros))
8645}
8646
8647fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
8658 Some(match ident.to_ascii_lowercase().as_str() {
8659 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
8660 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
8661 "bigint" => ColumnTypeName::BigInt,
8662 "float" | "double" | "real" => ColumnTypeName::Float,
8663 "text" => ColumnTypeName::Text,
8664 "bool" | "boolean" => ColumnTypeName::Bool,
8665 "date" => ColumnTypeName::Date,
8666 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
8667 "timestamptz" => ColumnTypeName::Timestamptz,
8668 "json" => ColumnTypeName::Json,
8669 "jsonb" => ColumnTypeName::Jsonb,
8670 "bytea" | "bytes" => ColumnTypeName::Bytes,
8671 "tsvector" => ColumnTypeName::TsVector,
8672 "tsquery" => ColumnTypeName::TsQuery,
8673 "uuid" => ColumnTypeName::Uuid,
8674 "time" => ColumnTypeName::Time,
8675 "year" => ColumnTypeName::Year,
8676 "timetz" => ColumnTypeName::TimeTz,
8677 "money" => ColumnTypeName::Money,
8678 _ => return None,
8679 })
8680}
8681
8682pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8709 parse_plpgsql_body(body)
8710}
8711
8712fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
8713 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
8717 message: alloc::format!("plpgsql body lex error: {e}"),
8718 token_pos: 0,
8719 })?;
8720 let mut parser = Parser::new(tokens);
8721 parser.parse_plpgsql_block()
8722}
8723
8724#[cfg(test)]
8725mod tests {
8726 use super::*;
8727 use alloc::string::ToString;
8728
8729 fn parse(s: &str) -> Statement {
8730 parse_statement(s).expect("parse ok")
8731 }
8732
8733 fn lit_int(n: i64) -> Expr {
8734 Expr::Literal(Literal::Integer(n))
8735 }
8736
8737 fn col(name: &str) -> Expr {
8738 Expr::Column(ColumnName {
8739 qualifier: None,
8740 name: name.into(),
8741 })
8742 }
8743
8744 #[test]
8745 fn select_single_integer() {
8746 let s = parse("SELECT 1");
8747 let Statement::Select(s) = s else {
8748 panic!("expected SELECT")
8749 };
8750 assert_eq!(s.items.len(), 1);
8751 assert!(s.from.is_none());
8752 assert!(s.where_.is_none());
8753 }
8754
8755 #[test]
8756 fn select_multiple_literal_kinds() {
8757 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
8758 let Statement::Select(s) = s else {
8759 panic!("expected SELECT")
8760 };
8761 assert_eq!(s.items.len(), 5);
8762 }
8763
8764 #[test]
8765 fn select_wildcard_from_table() {
8766 let s = parse("SELECT * FROM users");
8767 let Statement::Select(s) = s else {
8768 panic!("expected SELECT")
8769 };
8770 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
8771 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
8772 }
8773
8774 #[test]
8775 fn select_with_table_alias() {
8776 let s = parse("SELECT * FROM users AS u");
8777 let Statement::Select(s) = s else {
8778 panic!("expected SELECT")
8779 };
8780 let t = &s.from.as_ref().unwrap().primary;
8781 assert_eq!(t.name, "users");
8782 assert_eq!(t.alias.as_deref(), Some("u"));
8783 }
8784
8785 #[test]
8786 fn select_with_where_eq() {
8787 let s = parse("SELECT a FROM t WHERE a = 1");
8788 let Statement::Select(s) = s else {
8789 panic!("expected SELECT")
8790 };
8791 let w = s.where_.unwrap();
8792 assert_eq!(
8793 w,
8794 Expr::Binary {
8795 lhs: Box::new(col("a")),
8796 op: BinOp::Eq,
8797 rhs: Box::new(lit_int(1)),
8798 }
8799 );
8800 }
8801
8802 #[test]
8803 fn arithmetic_precedence() {
8804 let s = parse("SELECT 1 + 2 * 3");
8805 let Statement::Select(s) = s else {
8806 panic!("expected SELECT")
8807 };
8808 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8809 panic!("wildcard?")
8810 };
8811 assert_eq!(
8812 expr,
8813 &Expr::Binary {
8814 lhs: Box::new(lit_int(1)),
8815 op: BinOp::Add,
8816 rhs: Box::new(Expr::Binary {
8817 lhs: Box::new(lit_int(2)),
8818 op: BinOp::Mul,
8819 rhs: Box::new(lit_int(3)),
8820 }),
8821 }
8822 );
8823 }
8824
8825 #[test]
8826 fn parentheses_override_precedence() {
8827 let s = parse("SELECT (1 + 2) * 3");
8828 let Statement::Select(s) = s else {
8829 panic!("expected SELECT")
8830 };
8831 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8832 panic!()
8833 };
8834 assert_eq!(
8835 expr,
8836 &Expr::Binary {
8837 lhs: Box::new(Expr::Binary {
8838 lhs: Box::new(lit_int(1)),
8839 op: BinOp::Add,
8840 rhs: Box::new(lit_int(2)),
8841 }),
8842 op: BinOp::Mul,
8843 rhs: Box::new(lit_int(3)),
8844 }
8845 );
8846 }
8847
8848 #[test]
8849 fn not_binds_below_comparison() {
8850 let s = parse("SELECT NOT a = 1 FROM t");
8852 let Statement::Select(s) = s else {
8853 panic!("expected SELECT")
8854 };
8855 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8856 panic!()
8857 };
8858 assert_eq!(
8859 expr,
8860 &Expr::Unary {
8861 op: UnOp::Not,
8862 expr: Box::new(Expr::Binary {
8863 lhs: Box::new(col("a")),
8864 op: BinOp::Eq,
8865 rhs: Box::new(lit_int(1)),
8866 }),
8867 }
8868 );
8869 }
8870
8871 #[test]
8872 fn unary_minus_binds_above_multiplication() {
8873 let s = parse("SELECT -a * 2 FROM t");
8875 let Statement::Select(s) = s else {
8876 panic!("expected SELECT")
8877 };
8878 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8879 panic!()
8880 };
8881 assert_eq!(
8882 expr,
8883 &Expr::Binary {
8884 lhs: Box::new(Expr::Unary {
8885 op: UnOp::Neg,
8886 expr: Box::new(col("a")),
8887 }),
8888 op: BinOp::Mul,
8889 rhs: Box::new(lit_int(2)),
8890 }
8891 );
8892 }
8893
8894 #[test]
8895 fn qualified_column() {
8896 let s = parse("SELECT t.col FROM t");
8897 let Statement::Select(s) = s else {
8898 panic!("expected SELECT")
8899 };
8900 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8901 panic!()
8902 };
8903 assert_eq!(
8904 expr,
8905 &Expr::Column(ColumnName {
8906 qualifier: Some("t".into()),
8907 name: "col".into()
8908 })
8909 );
8910 }
8911
8912 #[test]
8913 fn select_item_alias_with_as() {
8914 let s = parse("SELECT a AS y FROM t");
8915 let Statement::Select(s) = s else {
8916 panic!("expected SELECT")
8917 };
8918 let SelectItem::Expr { alias, .. } = &s.items[0] else {
8919 panic!()
8920 };
8921 assert_eq!(alias.as_deref(), Some("y"));
8922 }
8923
8924 #[test]
8925 fn trailing_semicolon_accepted() {
8926 let s = parse("SELECT 1;");
8927 let Statement::Select(s) = s else {
8928 panic!("expected SELECT")
8929 };
8930 assert_eq!(s.items.len(), 1);
8931 }
8932
8933 #[test]
8934 fn boolean_chain_with_and_or_not() {
8935 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
8937 let Statement::Select(s) = s else {
8938 panic!("expected SELECT")
8939 };
8940 let SelectItem::Expr { expr, .. } = &s.items[0] else {
8941 panic!()
8942 };
8943 let expected = Expr::Binary {
8944 lhs: Box::new(Expr::Unary {
8945 op: UnOp::Not,
8946 expr: Box::new(col("a")),
8947 }),
8948 op: BinOp::Or,
8949 rhs: Box::new(Expr::Binary {
8950 lhs: Box::new(col("b")),
8951 op: BinOp::And,
8952 rhs: Box::new(Expr::Unary {
8953 op: UnOp::Not,
8954 expr: Box::new(col("c")),
8955 }),
8956 }),
8957 };
8958 assert_eq!(expr, &expected);
8959 }
8960
8961 #[test]
8962 fn empty_input_errors() {
8963 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
8970 assert!(matches!(
8971 parse_statement(" \n\t ").unwrap(),
8972 Statement::Empty
8973 ));
8974 assert!(parse_statement("SELECT FROM WHERE").is_err());
8976 }
8977
8978 #[test]
8979 fn unmatched_paren_errors() {
8980 assert!(parse_statement("SELECT (1 + 2").is_err());
8981 }
8982
8983 #[test]
8984 fn display_round_trip_simple_select() {
8985 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
8986 let text = original.to_string();
8987 let again = parse_statement(&text).expect("re-parse");
8988 assert_eq!(original, again);
8989 }
8990
8991 #[test]
8994 fn create_table_single_column() {
8995 let s = parse("CREATE TABLE foo (a INT)");
8996 let Statement::CreateTable(c) = s else {
8997 panic!("expected CreateTable")
8998 };
8999 assert_eq!(c.name, "foo");
9000 assert_eq!(c.columns.len(), 1);
9001 assert_eq!(c.columns[0].name, "a");
9002 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9003 assert!(c.columns[0].nullable);
9004 }
9005
9006 #[test]
9007 fn create_table_multi_column_with_not_null_mix() {
9008 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
9009 let Statement::CreateTable(c) = s else {
9010 panic!()
9011 };
9012 assert_eq!(c.columns.len(), 4);
9013 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9014 assert!(!c.columns[0].nullable);
9015 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
9016 assert!(c.columns[1].nullable);
9017 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
9018 assert!(!c.columns[2].nullable);
9019 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
9020 }
9021
9022 #[test]
9023 fn create_table_bigint_supported() {
9024 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
9025 let Statement::CreateTable(c) = s else {
9026 panic!()
9027 };
9028 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
9029 }
9030
9031 #[test]
9032 fn create_table_vector_default_is_f32() {
9033 let s = parse("CREATE TABLE t (v VECTOR(128))");
9034 let Statement::CreateTable(c) = s else {
9035 panic!()
9036 };
9037 assert_eq!(
9038 c.columns[0].ty,
9039 ColumnTypeName::Vector {
9040 dim: 128,
9041 encoding: VecEncoding::F32,
9042 },
9043 );
9044 }
9045
9046 #[test]
9047 fn create_table_vector_using_sq8() {
9048 for sql in [
9051 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
9052 "CREATE TABLE t (v VECTOR(128) using sq8)",
9053 ] {
9054 let s = parse(sql);
9055 let Statement::CreateTable(c) = s else {
9056 panic!()
9057 };
9058 assert_eq!(
9059 c.columns[0].ty,
9060 ColumnTypeName::Vector {
9061 dim: 128,
9062 encoding: VecEncoding::Sq8,
9063 },
9064 "{sql}",
9065 );
9066 }
9067 }
9068
9069 #[test]
9070 fn create_table_vector_using_unknown_errors() {
9071 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
9080 assert!(
9081 err.message.contains("USING")
9082 || err.message.contains("using")
9083 || err.message.contains("')'")
9084 || err.message.contains("','"),
9085 "expected USING/column-list rejection, got: {}",
9086 err.message
9087 );
9088 }
9089
9090 #[test]
9091 fn vector_using_sq8_display_roundtrips() {
9092 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
9095 let Statement::CreateTable(c) = s else {
9096 panic!()
9097 };
9098 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9099 }
9100
9101 #[test]
9102 fn parser_recognises_placeholders() {
9103 use crate::ast::{Expr, SelectItem, Statement};
9104 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9106 let Statement::Select(sel) = s else { panic!() };
9107 assert!(matches!(
9108 sel.items[0],
9109 SelectItem::Expr {
9110 expr: Expr::Placeholder(1),
9111 alias: None
9112 }
9113 ));
9114 let SelectItem::Expr {
9116 expr: Expr::Binary { lhs, rhs, .. },
9117 ..
9118 } = &sel.items[1]
9119 else {
9120 panic!()
9121 };
9122 assert!(matches!(**lhs, Expr::Placeholder(2)));
9123 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9124 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9126 panic!()
9127 };
9128 assert!(matches!(**rhs, Expr::Placeholder(3)));
9129 }
9130
9131 #[test]
9132 fn parser_rejects_dollar_zero() {
9133 assert!(parse_statement("SELECT $0").is_err());
9135 }
9136
9137 #[test]
9138 fn placeholder_display_roundtrips() {
9139 let s = parse("SELECT $42 FROM t");
9142 let printed = s.to_string();
9143 assert!(printed.contains("$42"));
9144 let again = parse(&printed);
9145 assert_eq!(s, again);
9146 }
9147
9148 #[test]
9149 fn alter_index_rebuild_bare() {
9150 use crate::ast::{AlterIndexTarget, Statement};
9151 let s = parse("ALTER INDEX my_idx REBUILD");
9152 let Statement::AlterIndex(a) = s else {
9153 panic!("expected AlterIndex, got {s:?}")
9154 };
9155 assert_eq!(a.name, "my_idx");
9156 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9157 }
9158
9159 #[test]
9160 fn alter_index_rebuild_with_encoding() {
9161 use crate::ast::{AlterIndexTarget, Statement};
9162 for (sql, want) in [
9163 (
9164 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9165 VecEncoding::F32,
9166 ),
9167 (
9168 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9169 VecEncoding::Sq8,
9170 ),
9171 (
9172 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9173 VecEncoding::F16,
9174 ),
9175 ] {
9176 let s = parse(sql);
9177 let Statement::AlterIndex(a) = s else {
9178 panic!("{sql}: expected AlterIndex")
9179 };
9180 assert_eq!(a.name, "my_idx");
9181 assert_eq!(
9182 a.target,
9183 AlterIndexTarget::Rebuild {
9184 encoding: Some(want)
9185 },
9186 "{sql}"
9187 );
9188 }
9189 }
9190
9191 #[test]
9192 fn alter_index_rebuild_unknown_encoding_errors() {
9193 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9194 assert!(
9195 err.message.contains("unknown vector encoding"),
9196 "got: {}",
9197 err.message
9198 );
9199 }
9200
9201 #[test]
9202 fn alter_index_rebuild_display_roundtrips() {
9203 for (input, want) in [
9204 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9205 (
9206 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9207 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9208 ),
9209 (
9210 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9211 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9212 ),
9213 ] {
9214 let s = parse(input);
9215 assert_eq!(s.to_string(), want);
9216 }
9217 }
9218
9219 #[test]
9220 fn create_table_unknown_type_defers_to_engine() {
9221 let stmt = parse_statement("CREATE TABLE x (a xml)").unwrap();
9228 let Statement::CreateTable(t) = stmt else {
9229 panic!("expected CreateTable");
9230 };
9231 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("xml"));
9232 }
9233
9234 #[test]
9235 fn create_table_missing_table_keyword_errors() {
9236 assert!(parse_statement("CREATE x (a INT)").is_err());
9237 }
9238
9239 #[test]
9240 fn insert_single_value() {
9241 let s = parse("INSERT INTO foo VALUES (42)");
9242 let Statement::Insert(i) = s else {
9243 panic!("expected Insert")
9244 };
9245 assert_eq!(i.table, "foo");
9246 assert_eq!(i.rows.len(), 1);
9247 assert_eq!(i.rows[0].len(), 1);
9248 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9249 }
9250
9251 #[test]
9252 fn insert_multi_value_with_mixed_literals() {
9253 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9254 let Statement::Insert(i) = s else { panic!() };
9255 assert_eq!(i.rows.len(), 1);
9256 assert_eq!(i.rows[0].len(), 5);
9257 }
9258
9259 #[test]
9260 fn insert_missing_into_errors() {
9261 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9262 }
9263
9264 #[test]
9265 fn create_table_round_trip() {
9266 let original =
9267 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9268 let text = original.to_string();
9269 let again = parse_statement(&text).expect("re-parse");
9270 assert_eq!(original, again);
9271 }
9272
9273 #[test]
9274 fn insert_round_trip_with_negation_and_string() {
9275 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9276 let text = original.to_string();
9277 let again = parse_statement(&text).expect("re-parse");
9278 assert_eq!(original, again);
9279 }
9280
9281 #[test]
9282 fn unknown_keyword_at_statement_start_errors() {
9283 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9286 assert!(err.message.contains("expected SELECT"));
9287 }
9288
9289 #[test]
9292 fn create_index_basic() {
9293 let s = parse("CREATE INDEX idx_id ON users (id)");
9294 let Statement::CreateIndex(c) = s else {
9295 panic!("expected CreateIndex")
9296 };
9297 assert_eq!(c.name, "idx_id");
9298 assert_eq!(c.table, "users");
9299 assert_eq!(c.column, "id");
9300 }
9301
9302 #[test]
9303 fn create_index_missing_on_errors() {
9304 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9305 }
9306
9307 #[test]
9308 fn create_index_missing_paren_errors() {
9309 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9310 }
9311
9312 #[test]
9313 fn create_index_round_trip() {
9314 let original = parse("CREATE INDEX by_name ON users (name)");
9315 let again = parse_statement(&original.to_string()).unwrap();
9316 assert_eq!(original, again);
9317 }
9318
9319 #[test]
9322 fn create_unique_index_basic() {
9323 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9324 let Statement::CreateIndex(c) = s else {
9325 panic!("expected CreateIndex");
9326 };
9327 assert!(c.is_unique);
9328 assert_eq!(c.column, "a");
9329 assert!(c.partial_predicate.is_none());
9330 }
9331
9332 #[test]
9333 fn create_unique_index_partial() {
9334 let s = parse(
9336 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9337 ON email_templates (user_address) WHERE is_default = true",
9338 );
9339 let Statement::CreateIndex(c) = s else {
9340 panic!("expected CreateIndex");
9341 };
9342 assert!(c.is_unique);
9343 assert_eq!(c.table, "email_templates");
9344 assert_eq!(c.column, "user_address");
9345 assert!(c.partial_predicate.is_some());
9346 }
9347
9348 #[test]
9349 fn create_unique_index_composite_with_predicate() {
9350 let s = parse(
9352 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9353 ON calendar_events (calendar_id, uid, recurrence_id) \
9354 WHERE recurrence_id IS NOT NULL",
9355 );
9356 let Statement::CreateIndex(c) = s else {
9357 panic!("expected CreateIndex");
9358 };
9359 assert!(c.is_unique);
9360 assert_eq!(c.column, "calendar_id");
9361 assert_eq!(
9362 c.extra_columns,
9363 vec!["uid".to_string(), "recurrence_id".to_string()]
9364 );
9365 assert!(c.partial_predicate.is_some());
9366 }
9367
9368 #[test]
9369 fn create_unique_index_using_btree_ok() {
9370 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9371 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9372 }
9373
9374 #[test]
9375 fn create_unique_index_using_hnsw_rejected() {
9376 let err =
9377 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9378 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9379 }
9380
9381 #[test]
9382 fn create_unique_index_round_trip() {
9383 let original = parse(
9384 "CREATE UNIQUE INDEX uq_calendar_events_master \
9385 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9386 );
9387 let again = parse_statement(&original.to_string()).unwrap();
9388 assert_eq!(original, again);
9389 }
9390
9391 #[test]
9392 fn create_unique_without_index_errors() {
9393 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9394 assert!(err.message.contains("INDEX"), "{}", err.message);
9395 }
9396
9397 #[test]
9400 fn create_table_bytea_column() {
9401 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9402 let Statement::CreateTable(c) = s else {
9403 panic!("expected CreateTable");
9404 };
9405 assert_eq!(c.columns.len(), 2);
9406 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9407 assert!(!c.columns[1].nullable);
9408 }
9409
9410 #[test]
9411 fn create_table_bytes_alias_column() {
9412 let s = parse("CREATE TABLE t (blob BYTES)");
9413 let Statement::CreateTable(c) = s else {
9414 panic!("expected CreateTable");
9415 };
9416 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9417 }
9418
9419 #[test]
9420 fn bytea_round_trip_display() {
9421 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9422 let again = parse_statement(&original.to_string()).unwrap();
9423 assert_eq!(original, again);
9424 }
9425
9426 #[test]
9429 fn begin_commit_rollback_parse_as_unit_variants() {
9430 assert_eq!(parse("BEGIN"), Statement::Begin);
9431 assert_eq!(parse("COMMIT"), Statement::Commit);
9432 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9433 assert_eq!(parse("BEGIN;"), Statement::Begin);
9435 }
9436
9437 #[test]
9440 fn inner_product_binop_parses() {
9441 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9442 let Statement::Select(s) = s else { panic!() };
9443 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9444 panic!()
9445 };
9446 assert!(matches!(
9447 expr,
9448 Expr::Binary {
9449 op: BinOp::InnerProduct,
9450 ..
9451 }
9452 ));
9453 }
9454
9455 #[test]
9456 fn cosine_distance_binop_parses() {
9457 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9458 let Statement::Select(s) = s else { panic!() };
9459 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9460 panic!()
9461 };
9462 assert!(matches!(
9463 expr,
9464 Expr::Binary {
9465 op: BinOp::CosineDistance,
9466 ..
9467 }
9468 ));
9469 }
9470
9471 #[test]
9472 fn vector_cast_postfix_wraps_string_literal() {
9473 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9474 let Statement::Select(s) = s else { panic!() };
9475 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9476 panic!()
9477 };
9478 assert!(matches!(
9479 expr,
9480 Expr::Cast {
9481 target: CastTarget::Vector,
9482 ..
9483 }
9484 ));
9485 }
9486
9487 #[test]
9488 fn unsupported_cast_target_errors() {
9489 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
9491 assert!(err.message.contains("unsupported cast target"));
9492 }
9493
9494 #[test]
9495 fn tx_statements_round_trip() {
9496 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
9497 let original = parse(q);
9498 let again = parse_statement(&original.to_string()).unwrap();
9499 assert_eq!(original, again);
9500 }
9501 }
9502
9503 #[test]
9504 fn interval_text_parsing_units() {
9505 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
9507 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
9508 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
9509 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
9510 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
9512 assert_eq!(
9513 parse_interval_text("1 day 2 hours"),
9514 Some((0, 86_400_000_000 + 7_200_000_000))
9515 );
9516 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
9518 assert_eq!(parse_interval_text(""), None);
9520 assert_eq!(parse_interval_text("garbage"), None);
9521 assert_eq!(parse_interval_text("1 fortnight"), None);
9522 assert_eq!(parse_interval_text("1"), None);
9523 }
9524
9525 #[test]
9526 fn interval_literal_roundtrips_via_display() {
9527 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
9528 let s = parsed.to_string();
9529 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
9531 let again = parse_statement(&s).unwrap();
9533 assert_eq!(parsed, again);
9534 }
9535
9536 #[test]
9539 fn parser_recognises_create_publication_bare() {
9540 let s = parse("CREATE PUBLICATION pub_a");
9541 let Statement::CreatePublication(p) = s else {
9542 panic!("expected CreatePublication, got {s:?}")
9543 };
9544 assert_eq!(p.name, "pub_a");
9545 assert_eq!(p.scope, PublicationScope::AllTables);
9546 }
9547
9548 #[test]
9549 fn parser_recognises_create_publication_for_all_tables() {
9550 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
9551 let Statement::CreatePublication(p) = s else {
9552 panic!("expected CreatePublication, got {s:?}")
9553 };
9554 assert_eq!(p.name, "pub_a");
9555 assert_eq!(p.scope, PublicationScope::AllTables);
9556 }
9557
9558 #[test]
9559 fn parser_recognises_drop_publication() {
9560 let s = parse("DROP PUBLICATION pub_a");
9561 let Statement::DropPublication(name) = s else {
9562 panic!("expected DropPublication, got {s:?}")
9563 };
9564 assert_eq!(name, "pub_a");
9565 }
9566
9567 #[test]
9568 fn parser_recognises_for_table_list() {
9569 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
9570 let Statement::CreatePublication(p) = s else {
9571 panic!("expected CreatePublication, got {s:?}")
9572 };
9573 assert_eq!(p.name, "pub_a");
9574 let PublicationScope::ForTables(ts) = p.scope else {
9575 panic!("expected ForTables scope")
9576 };
9577 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
9578 }
9579
9580 #[test]
9581 fn parser_recognises_for_tables_plural() {
9582 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
9584 let Statement::CreatePublication(p) = s else {
9585 panic!("expected CreatePublication, got {s:?}")
9586 };
9587 let PublicationScope::ForTables(ts) = p.scope else {
9588 panic!("expected ForTables")
9589 };
9590 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9591 }
9592
9593 #[test]
9594 fn parser_recognises_for_all_tables_except_list() {
9595 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
9596 let Statement::CreatePublication(p) = s else {
9597 panic!()
9598 };
9599 let PublicationScope::AllTablesExcept(ts) = p.scope else {
9600 panic!("expected AllTablesExcept")
9601 };
9602 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9603 }
9604
9605 #[test]
9606 fn parser_rejects_for_table_with_empty_list() {
9607 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
9609 .expect_err("must error on empty list");
9610 assert!(!err.message.is_empty());
9613 }
9614
9615 #[test]
9616 fn parser_recognises_show_publications() {
9617 let s = parse("SHOW PUBLICATIONS");
9620 assert!(matches!(s, Statement::ShowPublications));
9621 }
9622
9623 #[test]
9626 fn parser_recognises_create_subscription_single_publication() {
9627 let s = parse(
9628 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
9629 );
9630 let Statement::CreateSubscription(c) = s else {
9631 panic!("expected CreateSubscription, got {s:?}")
9632 };
9633 assert_eq!(c.name, "sub_a");
9634 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
9635 assert_eq!(c.publications, alloc::vec!["pub_a"]);
9636 }
9637
9638 #[test]
9639 fn parser_recognises_create_subscription_multi_publication() {
9640 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
9641 let Statement::CreateSubscription(c) = s else {
9642 panic!()
9643 };
9644 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
9645 }
9646
9647 #[test]
9648 fn parser_rejects_create_subscription_missing_connection() {
9649 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
9650 .expect_err("must error on missing CONNECTION");
9651 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
9652 }
9653
9654 #[test]
9655 fn parser_rejects_create_subscription_missing_publication() {
9656 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
9657 .expect_err("must error on missing PUBLICATION");
9658 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
9659 }
9660
9661 #[test]
9662 fn parser_recognises_drop_subscription() {
9663 let s = parse("DROP SUBSCRIPTION sub_a");
9664 let Statement::DropSubscription(name) = s else {
9665 panic!("expected DropSubscription, got {s:?}")
9666 };
9667 assert_eq!(name, "sub_a");
9668 }
9669
9670 #[test]
9671 fn parser_recognises_show_subscriptions() {
9672 let s = parse("SHOW SUBSCRIPTIONS");
9673 assert!(matches!(s, Statement::ShowSubscriptions));
9674 }
9675
9676 #[test]
9677 fn parser_recognises_wait_for_wal_position_no_timeout() {
9678 let s = parse("WAIT FOR WAL POSITION 12345");
9679 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9680 panic!("expected WaitForWalPosition, got {s:?}")
9681 };
9682 assert_eq!(pos, 12345);
9683 assert!(timeout_ms.is_none());
9684 }
9685
9686 #[test]
9687 fn parser_recognises_wait_for_wal_position_with_timeout() {
9688 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
9689 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9690 panic!()
9691 };
9692 assert_eq!(pos, 67890);
9693 assert_eq!(timeout_ms, Some(5000));
9694 }
9695
9696 #[test]
9697 fn parser_rejects_wait_with_negative_position() {
9698 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
9704 assert!(!err.message.is_empty());
9705 }
9706
9707 #[test]
9708 fn parser_recognises_bare_analyze() {
9709 let s = parse("ANALYZE");
9710 assert!(matches!(s, Statement::Analyze(None)));
9711 }
9712
9713 #[test]
9714 fn parser_recognises_analyze_with_table() {
9715 let s = parse("ANALYZE users");
9716 let Statement::Analyze(Some(name)) = s else {
9717 panic!("expected Analyze, got {s:?}")
9718 };
9719 assert_eq!(name, "users");
9720 }
9721
9722 #[test]
9723 fn parser_recognises_analyze_with_quoted_table() {
9724 let s = parse("ANALYZE \"Mixed Case\"");
9725 let Statement::Analyze(Some(name)) = s else {
9726 panic!()
9727 };
9728 assert_eq!(name, "Mixed Case");
9729 }
9730
9731 #[test]
9732 fn parser_rejects_analyze_with_garbage_token() {
9733 let err = parse_statement("ANALYZE 42").expect_err("must error");
9734 assert!(!err.message.is_empty());
9735 }
9736
9737 #[test]
9738 fn analyze_display_roundtrips() {
9739 for sql in ["ANALYZE", "ANALYZE users"] {
9740 let s = parse(sql);
9741 let printed = s.to_string();
9742 let again = parse_statement(&printed)
9743 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9744 assert_eq!(s, again);
9745 }
9746 }
9747
9748 #[test]
9749 fn wait_for_display_roundtrips() {
9750 for sql in [
9751 "WAIT FOR WAL POSITION 12345",
9752 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
9753 ] {
9754 let s = parse(sql);
9755 let printed = s.to_string();
9756 let again = parse_statement(&printed)
9757 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9758 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9759 }
9760 }
9761
9762 #[test]
9763 fn subscription_ddl_display_roundtrips() {
9764 for sql in [
9765 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
9766 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
9767 "DROP SUBSCRIPTION sub_a",
9768 "SHOW SUBSCRIPTIONS",
9769 ] {
9770 let s = parse(sql);
9771 let printed = s.to_string();
9772 let again = parse_statement(&printed)
9773 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9774 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9775 }
9776 }
9777
9778 #[test]
9779 fn parser_drop_dispatches_user_vs_publication() {
9780 let s = parse("DROP USER 'alice'");
9783 let Statement::DropUser(name) = s else {
9784 panic!("expected DropUser, got {s:?}")
9785 };
9786 assert_eq!(name, "alice");
9787 let s = parse("DROP PUBLICATION p1");
9789 assert!(matches!(s, Statement::DropPublication(_)));
9790 }
9791
9792 #[test]
9793 fn publication_ddl_display_roundtrips() {
9794 for sql in [
9797 "CREATE PUBLICATION pub_a",
9798 "CREATE PUBLICATION pub_a FOR ALL TABLES",
9799 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
9800 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
9801 "DROP PUBLICATION pub_a",
9802 "SHOW PUBLICATIONS",
9803 ] {
9804 let s = parse(sql);
9805 let printed = s.to_string();
9806 let again = parse_statement(&printed)
9807 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9808 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9809 }
9810 }
9811
9812 #[test]
9815 fn create_function_returns_trigger_plpgsql_minimal() {
9816 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
9817 let s = parse(sql);
9818 let Statement::CreateFunction(f) = s else {
9819 panic!("expected CreateFunction");
9820 };
9821 assert_eq!(f.name, "noop");
9822 assert!(!f.or_replace);
9823 assert!(f.args.is_empty());
9824 assert!(matches!(f.returns, FunctionReturn::Trigger));
9825 assert_eq!(f.language, "plpgsql");
9826 let FunctionBody::PlPgSql(block) = f.body else {
9827 panic!("expected PlPgSql body");
9828 };
9829 assert_eq!(block.statements.len(), 1);
9830 assert!(matches!(
9831 block.statements[0],
9832 PlPgSqlStmt::Return(ReturnTarget::New)
9833 ));
9834 }
9835
9836 #[test]
9837 fn create_function_or_replace_with_assignment() {
9838 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
9841BEGIN
9842 NEW.search_vector := to_tsvector('english', NEW.subject);
9843 RETURN NEW;
9844END;
9845$$";
9846 let s = parse(sql);
9847 let Statement::CreateFunction(f) = s else {
9848 panic!("expected CreateFunction");
9849 };
9850 assert!(f.or_replace);
9851 let FunctionBody::PlPgSql(block) = &f.body else {
9852 panic!("expected PlPgSql body");
9853 };
9854 assert_eq!(block.statements.len(), 2);
9855 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
9857 panic!("expected Assign as first stmt");
9858 };
9859 match target {
9860 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
9861 other => panic!("expected NEW.col, got {other:?}"),
9862 }
9863 assert!(matches!(
9865 block.statements[1],
9866 PlPgSqlStmt::Return(ReturnTarget::New)
9867 ));
9868 }
9869
9870 #[test]
9871 fn create_trigger_after_insert_or_update() {
9872 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
9873 let s = parse(sql);
9874 let Statement::CreateTrigger(t) = s else {
9875 panic!("expected CreateTrigger");
9876 };
9877 assert_eq!(t.name, "tg");
9878 assert_eq!(t.table, "messages");
9879 assert_eq!(t.timing, TriggerTiming::After);
9880 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
9881 assert_eq!(t.for_each, TriggerForEach::Row);
9882 assert_eq!(t.function, "update_sv");
9883 }
9884
9885 #[test]
9886 fn create_trigger_before_delete_execute_procedure_alias() {
9887 let sql =
9889 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
9890 let s = parse(sql);
9891 let Statement::CreateTrigger(t) = s else {
9892 panic!("expected CreateTrigger");
9893 };
9894 assert_eq!(t.timing, TriggerTiming::Before);
9895 assert_eq!(t.events, vec![TriggerEvent::Delete]);
9896 }
9897
9898 #[test]
9899 fn drop_trigger_if_exists_round_trips() {
9900 let s = Statement::DropTrigger {
9905 name: "tg".into(),
9906 table: "messages".into(),
9907 if_exists: true,
9908 };
9909 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
9910 }
9911
9912 #[test]
9913 fn trigger_ddl_display_roundtrips_through_parser() {
9914 for sql in [
9918 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
9919 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
9920 ] {
9921 let s = parse(sql);
9922 let printed = s.to_string();
9923 let again = parse_statement(&printed)
9924 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
9925 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
9926 }
9927 }
9928}