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, 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, ReturnTarget, SelectItem, SelectStatement, Statement, TableRef,
28 TriggerEvent, TriggerForEach, TriggerTiming, UnOp, UnionKind, VecEncoding, WindowFrame,
29};
30use crate::lexer::{self, LexError, Token};
31
32fn is_dump_noise_statement(lc: &str) -> bool {
38 matches!(
39 lc,
40 "comment"
43 | "grant"
44 | "revoke"
45 | "lock"
47 | "unlock"
48 | "optimize"
52 | "check"
53 | "use"
54 | "\\restrict"
59 | "\\unrestrict"
60 )
61}
62
63fn is_vector_opclass_name(name: &str) -> bool {
71 let lc = name.to_ascii_lowercase();
72 matches!(
73 lc.as_str(),
74 "vector_cosine_ops"
75 | "vector_l2_ops"
76 | "vector_ip_ops"
77 | "halfvec_cosine_ops"
78 | "halfvec_l2_ops"
79 | "halfvec_ip_ops"
80 | "sq8_cosine_ops"
81 | "sq8_l2_ops"
82 | "sq8_ip_ops"
83 | "gin_trgm_ops"
89 | "gist_trgm_ops"
90 | "text_pattern_ops"
95 | "varchar_pattern_ops"
96 | "bpchar_pattern_ops"
97 | "int4_ops"
98 | "int8_ops"
99 | "text_ops"
100 )
101}
102
103#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct ParseError {
105 pub message: String,
106 pub token_pos: usize,
108}
109
110impl fmt::Display for ParseError {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 write!(
113 f,
114 "parse error at token #{}: {}",
115 self.token_pos, self.message
116 )
117 }
118}
119
120impl From<LexError> for ParseError {
121 fn from(e: LexError) -> Self {
122 Self {
123 message: format!("lex: {e}"),
124 token_pos: 0,
125 }
126 }
127}
128
129pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
135 let tokens = lexer::tokenize(input)?;
136 let mut p = Parser::new(tokens);
137 let expr = p.parse_expr(0)?;
138 p.expect_eof()?;
139 Ok(expr)
140}
141
142pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
145 let tokens = lexer::tokenize(input)?;
146 let mut p = Parser::new(tokens);
147 let stmt = p.parse_one_statement()?;
148 if matches!(p.peek(), Token::Semicolon) {
149 p.advance();
150 }
151 p.expect_eof()?;
152 Ok(stmt)
153}
154
155struct Parser {
156 tokens: Vec<Token>,
157 pos: usize,
158}
159
160impl Parser {
161 fn new(tokens: Vec<Token>) -> Self {
162 Self { tokens, pos: 0 }
163 }
164
165 fn peek(&self) -> &Token {
166 &self.tokens[self.pos]
168 }
169
170 fn advance(&mut self) -> Token {
171 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
172 if self.pos + 1 < self.tokens.len() {
173 self.pos += 1;
174 }
175 t
176 }
177
178 fn err(&self, message: String) -> ParseError {
179 ParseError {
180 message,
181 token_pos: self.pos,
182 }
183 }
184
185 fn expect_eof(&self) -> Result<(), ParseError> {
186 if matches!(self.peek(), Token::Eof) {
187 Ok(())
188 } else {
189 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
190 }
191 }
192
193 fn consume_until_statement_boundary(&mut self) {
198 loop {
199 match self.peek() {
200 Token::Semicolon | Token::Eof => return,
201 _ => self.advance(),
202 };
203 }
204 }
205
206 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
207 let first = match self.advance() {
208 Token::Ident(s) | Token::QuotedIdent(s) => s,
209 other => {
210 return Err(ParseError {
211 message: format!("expected identifier, got {other:?}"),
212 token_pos: self.pos.saturating_sub(1),
213 });
214 }
215 };
216 if matches!(self.peek(), Token::Dot) {
223 self.advance();
224 match self.advance() {
225 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
226 other => {
227 return Err(ParseError {
228 message: format!(
229 "expected identifier after '{first}.', got {other:?}"
230 ),
231 token_pos: self.pos.saturating_sub(1),
232 });
233 }
234 }
235 }
236 Ok(first)
237 }
238
239 #[allow(clippy::too_many_lines)]
240 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
241 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
249 return Ok(Statement::Empty);
250 }
251 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
261 let lc = s.to_ascii_lowercase();
262 if is_dump_noise_statement(&lc) {
263 self.consume_until_statement_boundary();
264 return Ok(Statement::Empty);
265 }
266 }
267 match self.peek() {
268 Token::Select => self.parse_select_stmt(),
269 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
275 self.advance();
276 match self.advance() {
279 Token::String(_) => {}
280 other => {
281 return Err(self.err(alloc::format!(
282 "expected dollar-quoted body after DO, got {other:?}"
283 )));
284 }
285 }
286 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
288 self.advance();
289 let _ = self.expect_ident_like()?;
290 }
291 Ok(Statement::DoBlock)
292 }
293 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
297 self.advance();
298 self.parse_with_cte_then_select()
299 }
300 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
303 self.advance();
304 let mut analyze = false;
305 let mut suggest = false;
306 if matches!(self.peek(), Token::LParen) {
308 self.advance();
309 let opt = match self.peek().clone() {
310 Token::Ident(s) | Token::QuotedIdent(s) => s,
311 other => {
312 return Err(self.err(format!(
313 "expected option keyword inside EXPLAIN (…), got {other:?}"
314 )));
315 }
316 };
317 if !opt.eq_ignore_ascii_case("suggest") {
318 return Err(self.err(format!(
319 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
320 )));
321 }
322 self.advance();
323 if !matches!(self.peek(), Token::RParen) {
324 return Err(self.err(format!(
325 "expected ')' after EXPLAIN option, got {:?}",
326 self.peek()
327 )));
328 }
329 self.advance();
330 suggest = true;
331 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
332 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
333 {
334 self.advance();
335 analyze = true;
336 }
337 let inner = self.parse_select_stmt()?;
338 let Statement::Select(s) = inner else {
339 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
340 };
341 Ok(Statement::Explain(crate::ast::ExplainStatement {
342 analyze,
343 inner: Box::new(s),
344 suggest,
345 }))
346 }
347 Token::Create => self.parse_create_stmt(),
348 Token::Insert => self.parse_insert_stmt(),
349 Token::Begin => {
350 self.advance();
351 Ok(Statement::Begin)
352 }
353 Token::Commit => {
354 self.advance();
355 Ok(Statement::Commit)
356 }
357 Token::Rollback => {
358 self.advance();
359 if matches!(self.peek(), Token::To) {
363 self.advance();
364 if matches!(self.peek(), Token::Savepoint) {
365 self.advance();
366 }
367 let name = self.expect_ident_like()?;
368 Ok(Statement::RollbackToSavepoint(name))
369 } else {
370 Ok(Statement::Rollback)
371 }
372 }
373 Token::Savepoint => {
374 self.advance();
375 let name = self.expect_ident_like()?;
376 Ok(Statement::Savepoint(name))
377 }
378 Token::Release => {
379 self.advance();
380 if matches!(self.peek(), Token::Savepoint) {
383 self.advance();
384 }
385 let name = self.expect_ident_like()?;
386 Ok(Statement::ReleaseSavepoint(name))
387 }
388 Token::Show => {
389 self.advance();
390 let target = match self.advance() {
396 Token::Tables => "tables".to_string(),
397 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
398 other => {
399 return Err(self.err(format!(
400 "expected SHOW target, got {other:?}"
401 )));
402 }
403 };
404 match target.as_str() {
405 "tables" => Ok(Statement::ShowTables),
406 "users" => Ok(Statement::ShowUsers),
407 "publications" => Ok(Statement::ShowPublications),
412 "subscriptions" => Ok(Statement::ShowSubscriptions),
414 "columns" => {
415 if !matches!(self.peek(), Token::From) {
416 return Err(self.err(format!(
417 "expected FROM after SHOW COLUMNS, got {:?}",
418 self.peek()
419 )));
420 }
421 self.advance();
422 let table = self.expect_ident_like()?;
423 Ok(Statement::ShowColumns(table))
424 }
425 other => Err(self.err(format!(
426 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
427 ))),
428 }
429 }
430 Token::Drop => {
436 self.advance();
437 match self.peek() {
438 Token::Publication => {
439 self.advance();
440 let name = self.expect_ident_or_string()?;
441 Ok(Statement::DropPublication(name))
442 }
443 Token::Subscription => {
444 self.advance();
445 let name = self.expect_ident_or_string()?;
446 Ok(Statement::DropSubscription(name))
447 }
448 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
449 self.advance();
450 let name = self.expect_ident_or_string()?;
451 Ok(Statement::DropUser(name))
452 }
453 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
455 self.advance();
456 let if_exists = self.consume_if_exists();
457 let name = self.expect_ident_like()?;
458 if !matches!(self.peek(), Token::On) {
460 return Err(self.err(alloc::format!(
461 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
462 self.peek()
463 )));
464 }
465 self.advance();
466 let table = self.expect_ident_like()?;
467 Ok(Statement::DropTrigger {
468 name,
469 table,
470 if_exists,
471 })
472 }
473 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
477 self.advance();
478 let if_exists = self.consume_if_exists();
479 let name = self.expect_ident_like()?;
480 if matches!(self.peek(), Token::LParen) {
482 self.advance();
483 let mut depth = 1usize;
485 while depth > 0 {
486 match self.peek() {
487 Token::LParen => depth += 1,
488 Token::RParen => depth -= 1,
489 Token::Eof => {
490 return Err(self.err(alloc::format!(
491 "unterminated arg list in DROP FUNCTION {name:?}"
492 )));
493 }
494 _ => {}
495 }
496 self.advance();
497 }
498 }
499 Ok(Statement::DropFunction { name, if_exists })
500 }
501 Token::Table => {
509 self.advance();
510 let if_exists = self.consume_if_exists();
511 let mut names: Vec<String> = Vec::new();
512 loop {
513 names.push(self.expect_ident_like()?);
514 if matches!(self.peek(), Token::Comma) {
515 self.advance();
516 continue;
517 }
518 break;
519 }
520 if matches!(
521 self.peek(),
522 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
523 || s.eq_ignore_ascii_case("restrict")
524 ) {
525 self.advance();
526 }
527 Ok(Statement::DropTable { names, if_exists })
528 }
529 Token::Index => {
535 self.advance();
536 let if_exists = self.consume_if_exists();
537 let name = self.expect_ident_like()?;
538 if matches!(
539 self.peek(),
540 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
541 || s.eq_ignore_ascii_case("restrict")
542 ) {
543 self.advance();
544 }
545 Ok(Statement::DropIndex { name, if_exists })
546 }
547 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
552 self.advance();
553 let _ = self.consume_if_exists();
554 let _ = self.expect_ident_like()?;
555 if matches!(
556 self.peek(),
557 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
558 || s.eq_ignore_ascii_case("restrict")
559 ) {
560 self.advance();
561 }
562 Ok(Statement::Empty)
563 }
564 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
570 self.advance();
571 let _ = self.consume_if_exists();
572 let _ = self.expect_ident_like()?;
573 if matches!(
574 self.peek(),
575 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
576 || s.eq_ignore_ascii_case("restrict")
577 ) {
578 self.advance();
579 }
580 Ok(Statement::Empty)
581 }
582 other => Err(self.err(format!(
583 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
584 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
585 ))),
586 }
587 }
588 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
589 self.advance();
590 self.parse_update_after_keyword()
591 }
592 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
593 self.advance();
594 self.parse_delete_after_keyword()
595 }
596 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
600 self.advance();
601 self.parse_alter_after_keyword()
602 }
603 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
607 self.advance();
608 self.parse_wait_after_keyword()
609 }
610 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
621 self.advance();
622 let next = self.peek().clone();
623 let cold = match next {
624 Token::Ident(s) | Token::QuotedIdent(s) => s,
625 _ => {
626 return Err(
627 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
628 );
629 }
630 };
631 if !cold.eq_ignore_ascii_case("cold") {
632 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
633 }
634 self.advance();
635 let next = self.peek().clone();
636 let segments = match next {
637 Token::Ident(s) | Token::QuotedIdent(s) => s,
638 _ => {
639 return Err(self.err(format!(
640 "expected SEGMENTS after COMPACT COLD, got {:?}",
641 self.peek()
642 )));
643 }
644 };
645 if !segments.eq_ignore_ascii_case("segments") {
646 return Err(self.err(format!(
647 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
648 )));
649 }
650 self.advance();
651 Ok(Statement::CompactColdSegments)
652 }
653 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
654 self.advance();
655 let target = match self.peek() {
656 Token::Eof | Token::Semicolon => None,
657 Token::Ident(_) | Token::QuotedIdent(_) => {
658 Some(self.expect_ident_like()?)
659 }
660 other => {
661 return Err(self.err(format!(
662 "expected table name or end of statement after ANALYZE, got {other:?}"
663 )));
664 }
665 };
666 Ok(Statement::Analyze(target))
667 }
668 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
674 self.advance();
675 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"))
680 {
681 self.advance();
682 }
683 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
688 {
689 self.advance();
690 if matches!(
692 self.peek(),
693 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
694 ) {
695 self.advance();
696 }
697 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
699 {
700 self.advance();
701 if matches!(
702 self.peek(),
703 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
704 ) {
705 self.advance();
706 }
707 }
708 return Ok(Statement::Empty);
709 }
710 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
713 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
714 {
715 self.advance(); self.advance(); if matches!(
718 self.peek(),
719 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
720 ) {
721 self.advance();
722 }
723 return Ok(Statement::Empty);
724 }
725 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
730 loop {
731 let lhs = match self.peek().clone() {
732 Token::SessionVar(s) => {
733 self.advance();
734 s
735 }
736 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
737 other => {
738 return Err(self.err(format!(
739 "expected parameter name after SET, got {other:?}"
740 )));
741 }
742 };
743 match self.peek() {
745 Token::Eq => {
746 self.advance();
747 }
748 Token::To => {
749 self.advance();
750 }
751 other => {
752 return Err(self.err(format!(
753 "expected `=` or TO after SET {lhs}, got {other:?}"
754 )));
755 }
756 }
757 let value = self.parse_set_value()?;
758 pairs.push((lhs, value));
759 if matches!(self.peek(), Token::Comma) {
760 self.advance();
761 continue;
762 }
763 break;
764 }
765 if pairs.len() == 1 {
766 let (name, value) = pairs.into_iter().next().unwrap();
767 Ok(Statement::SetParameter { name, value })
768 } else {
769 Ok(Statement::SetParameterList(pairs))
770 }
771 }
772 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
774 self.advance();
775 match self.peek().clone() {
776 Token::All => {
777 self.advance();
778 Ok(Statement::ResetParameter(None))
779 }
780 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
781 self.advance();
782 Ok(Statement::ResetParameter(None))
783 }
784 _ => {
785 let name = self.parse_set_param_name()?;
786 Ok(Statement::ResetParameter(Some(name)))
787 }
788 }
789 }
790 other => Err(self.err(format!(
791 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
792 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
793 ))),
794 }
795 }
796
797 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
798 debug_assert!(matches!(self.peek(), Token::Create));
799 self.advance();
800 match self.peek() {
801 Token::Table => self.parse_create_table_stmt_after_create(),
802 Token::Index => self.parse_create_index_stmt_after_create(false),
803 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
810 self.advance();
811 if !matches!(self.peek(), Token::Index) {
812 return Err(self.err(alloc::format!(
813 "expected INDEX after CREATE UNIQUE, got {:?}",
814 self.peek()
815 )));
816 }
817 self.parse_create_index_stmt_after_create(true)
818 }
819 Token::Publication => {
820 self.advance();
821 self.parse_create_publication_after_keyword()
822 }
823 Token::Subscription => {
824 self.advance();
825 self.parse_create_subscription_after_keyword()
826 }
827 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
831 self.advance();
832 self.parse_create_user_after_keyword()
833 }
834 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
838 self.advance();
839 self.parse_create_extension_after_keyword()
840 }
841 Token::Or => {
847 self.advance();
848 let next = self.peek();
849 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
850 return Err(self.err(alloc::format!(
851 "expected REPLACE after CREATE OR, got {next:?}"
852 )));
853 };
854 if !s2.eq_ignore_ascii_case("replace") {
855 return Err(self.err(alloc::format!(
856 "expected REPLACE after CREATE OR, got {s2:?}"
857 )));
858 }
859 self.advance();
860 self.parse_create_function_or_trigger_after_or_replace(true)
861 }
862 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
863 self.advance();
864 self.parse_create_function_after_keyword(false)
865 }
866 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
867 self.advance();
868 self.parse_create_trigger_after_keyword(false)
869 }
870 Token::Ident(s) | Token::QuotedIdent(s)
876 if matches!(
877 s.to_ascii_lowercase().as_str(),
878 "sequence"
879 | "schema"
880 | "view"
881 | "materialized"
882 | "type"
883 | "domain"
884 | "database"
885 | "role"
886 | "policy"
887 | "operator"
888 | "cast"
889 | "rule"
890 | "aggregate"
891 | "language"
892 | "collation"
893 | "conversion"
894 ) =>
895 {
896 self.consume_until_statement_boundary();
897 return Ok(Statement::Empty);
898 }
899 other => Err(self.err(format!(
900 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
901 ))),
902 }
903 }
904
905 fn parse_create_function_or_trigger_after_or_replace(
910 &mut self,
911 or_replace: bool,
912 ) -> Result<Statement, ParseError> {
913 let tok = self.peek();
914 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
915 return Err(self.err(alloc::format!(
916 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {tok:?}"
917 )));
918 };
919 if s.eq_ignore_ascii_case("function") {
920 self.advance();
921 self.parse_create_function_after_keyword(or_replace)
922 } else if s.eq_ignore_ascii_case("trigger") {
923 self.advance();
924 self.parse_create_trigger_after_keyword(or_replace)
925 } else {
926 Err(self.err(alloc::format!(
927 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {s:?}"
928 )))
929 }
930 }
931
932 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
937 self.consume_if_not_exists();
939 let name = self.expect_ident_like()?;
940 loop {
943 match self.peek() {
944 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
945 self.advance();
946 continue;
947 }
948 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
949 self.advance();
950 let _ = self.expect_ident_like()?;
951 continue;
952 }
953 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
954 self.advance();
955 let _ = self.advance();
957 continue;
958 }
959 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
960 self.advance();
961 let _ = self.advance();
962 continue;
963 }
964 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
965 self.advance();
966 continue;
967 }
968 _ => break,
969 }
970 }
971 Ok(Statement::CreateExtension(name))
972 }
973
974 fn parse_create_function_after_keyword(
986 &mut self,
987 or_replace: bool,
988 ) -> Result<Statement, ParseError> {
989 let name = self.expect_ident_like()?;
990 if !matches!(self.peek(), Token::LParen) {
994 return Err(self.err(alloc::format!(
995 "expected '(' after function name {name:?}, got {:?}",
996 self.peek()
997 )));
998 }
999 self.advance();
1000 let args = self.parse_function_arg_list()?;
1001 let tok = self.peek();
1003 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1004 return Err(self.err(alloc::format!(
1005 "expected RETURNS after function arg list, got {tok:?}"
1006 )));
1007 };
1008 if !s.eq_ignore_ascii_case("returns") {
1009 return Err(self.err(alloc::format!(
1010 "expected RETURNS after function arg list, got {s:?}"
1011 )));
1012 }
1013 self.advance();
1014 let returns = self.parse_function_return()?;
1015 let mut language: Option<String> = self.parse_optional_language()?;
1018 if !matches!(self.peek(), Token::As) {
1022 return Err(self.err(alloc::format!(
1023 "expected AS before function body, got {:?}",
1024 self.peek()
1025 )));
1026 }
1027 self.advance();
1028 let body_text = match self.peek() {
1029 Token::String(s) => {
1030 let body = s.clone();
1031 self.advance();
1032 body
1033 }
1034 other => {
1035 return Err(self.err(alloc::format!(
1036 "expected $$-quoted function body after AS, got {other:?}"
1037 )));
1038 }
1039 };
1040 if language.is_none() {
1042 language = self.parse_optional_language()?;
1043 }
1044 let language = language.unwrap_or_else(|| String::from("sql"));
1045 let body = if language.eq_ignore_ascii_case("plpgsql") {
1050 match parse_plpgsql_body(&body_text) {
1051 Ok(block) => FunctionBody::PlPgSql(block),
1052 Err(_) => FunctionBody::Raw(body_text),
1058 }
1059 } else {
1060 FunctionBody::Raw(body_text)
1061 };
1062 Ok(Statement::CreateFunction(CreateFunctionStatement {
1063 name,
1064 or_replace,
1065 args,
1066 returns,
1067 language,
1068 body,
1069 }))
1070 }
1071
1072 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1076 let mut args: Vec<FunctionArg> = Vec::new();
1077 if matches!(self.peek(), Token::RParen) {
1078 self.advance();
1079 return Ok(args);
1080 }
1081 loop {
1082 let mode = if matches!(self.peek(), Token::In) {
1085 self.advance();
1086 FunctionArgMode::In
1087 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1088 {
1089 self.advance();
1090 FunctionArgMode::Out
1091 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1092 {
1093 self.advance();
1094 FunctionArgMode::InOut
1095 } else {
1096 FunctionArgMode::In
1097 };
1098 let (name, ty_token) = {
1104 let first = self.expect_ident_like()?;
1105 match self.peek() {
1108 Token::Ident(_) | Token::QuotedIdent(_) => {
1109 let ty = self.expect_ident_like()?;
1110 (Some(first), ty)
1111 }
1112 _ => (None, first),
1113 }
1114 };
1115 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1117 Some(t) => FunctionArgType::Typed(t),
1118 None => FunctionArgType::Raw(ty_token),
1119 };
1120 args.push(FunctionArg { mode, name, ty });
1121 match self.peek() {
1122 Token::Comma => {
1123 self.advance();
1124 continue;
1125 }
1126 Token::RParen => {
1127 self.advance();
1128 return Ok(args);
1129 }
1130 other => {
1131 return Err(self.err(alloc::format!(
1132 "expected , or ) in function arg list, got {other:?}"
1133 )));
1134 }
1135 }
1136 }
1137 }
1138
1139 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1140 let ident = self.expect_ident_like()?;
1141 if ident.eq_ignore_ascii_case("trigger") {
1142 return Ok(FunctionReturn::Trigger);
1143 }
1144 if ident.eq_ignore_ascii_case("void") {
1145 return Ok(FunctionReturn::Void);
1146 }
1147 match map_type_ident_to_column_type_name(&ident) {
1148 Some(t) => Ok(FunctionReturn::Type(t)),
1149 None => Ok(FunctionReturn::Other(ident)),
1150 }
1151 }
1152
1153 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1154 match self.peek() {
1155 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1156 self.advance();
1157 let lang = self.expect_ident_like()?;
1158 Ok(Some(lang.to_ascii_lowercase()))
1159 }
1160 _ => Ok(None),
1161 }
1162 }
1163
1164 fn parse_create_trigger_after_keyword(
1168 &mut self,
1169 or_replace: bool,
1170 ) -> Result<Statement, ParseError> {
1171 let name = self.expect_ident_like()?;
1172 let timing = {
1173 let ident = self.expect_ident_like()?;
1174 if ident.eq_ignore_ascii_case("before") {
1175 TriggerTiming::Before
1176 } else if ident.eq_ignore_ascii_case("after") {
1177 TriggerTiming::After
1178 } else if ident.eq_ignore_ascii_case("instead") {
1179 let next = self.expect_ident_like()?;
1180 if !next.eq_ignore_ascii_case("of") {
1181 return Err(self.err(alloc::format!(
1182 "expected OF after INSTEAD in trigger timing, got {next:?}"
1183 )));
1184 }
1185 TriggerTiming::InsteadOf
1186 } else {
1187 return Err(self.err(alloc::format!(
1188 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
1189 )));
1190 }
1191 };
1192 let mut events: Vec<TriggerEvent> = Vec::new();
1199 let mut update_columns: Vec<String> = Vec::new();
1200 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
1201 events.push(first_ev);
1202 if !first_cols.is_empty() {
1203 update_columns = first_cols;
1204 }
1205 while matches!(self.peek(), Token::Or) {
1206 self.advance();
1207 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
1208 events.push(ev);
1209 if !cols.is_empty() {
1210 if !update_columns.is_empty() {
1211 return Err(self.err(
1212 "CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into(),
1213 ));
1214 }
1215 update_columns = cols;
1216 }
1217 }
1218 let tok = self.peek();
1220 let Token::On = tok else {
1221 return Err(self.err(alloc::format!(
1222 "expected ON after trigger events, got {tok:?}"
1223 )));
1224 };
1225 self.advance();
1226 let table = self.expect_ident_like()?;
1227 if !matches!(self.peek(), Token::For) {
1231 return Err(self.err(alloc::format!(
1232 "expected FOR EACH ROW / STATEMENT, got {:?}",
1233 self.peek()
1234 )));
1235 }
1236 self.advance();
1237 let for_each = {
1238 let e = self.expect_ident_like()?;
1239 if !e.eq_ignore_ascii_case("each") {
1240 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
1241 }
1242 let unit = self.expect_ident_like()?;
1243 if unit.eq_ignore_ascii_case("row") {
1244 TriggerForEach::Row
1245 } else if unit.eq_ignore_ascii_case("statement") {
1246 TriggerForEach::Statement
1247 } else {
1248 return Err(self.err(alloc::format!(
1249 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
1250 )));
1251 }
1252 };
1253 let exec = self.expect_ident_like()?;
1255 if !exec.eq_ignore_ascii_case("execute") {
1256 return Err(self.err(alloc::format!(
1257 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
1258 )));
1259 }
1260 let fn_or_proc = self.expect_ident_like()?;
1261 if !(fn_or_proc.eq_ignore_ascii_case("function")
1262 || fn_or_proc.eq_ignore_ascii_case("procedure"))
1263 {
1264 return Err(self.err(alloc::format!(
1265 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
1266 )));
1267 }
1268 let function = self.expect_ident_like()?;
1269 if matches!(self.peek(), Token::LParen) {
1271 self.advance();
1272 if !matches!(self.peek(), Token::RParen) {
1273 return Err(self.err(alloc::format!(
1274 "v7.12.4 trigger function calls take no args; got {:?}",
1275 self.peek()
1276 )));
1277 }
1278 self.advance();
1279 }
1280 Ok(Statement::CreateTrigger(CreateTriggerStatement {
1281 name,
1282 or_replace,
1283 timing,
1284 events,
1285 table,
1286 for_each,
1287 function,
1288 update_columns,
1289 }))
1290 }
1291
1292 fn parse_trigger_event_with_optional_of(
1296 &mut self,
1297 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
1298 let ev = self.parse_trigger_event()?;
1299 if !matches!(ev, TriggerEvent::Update) {
1300 return Ok((ev, Vec::new()));
1301 }
1302 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
1304 return Ok((ev, Vec::new()));
1305 }
1306 self.advance(); let mut cols: Vec<String> = Vec::new();
1308 loop {
1309 cols.push(self.expect_ident_like()?);
1310 if matches!(self.peek(), Token::Comma) {
1311 self.advance();
1312 continue;
1313 }
1314 break;
1315 }
1316 if cols.is_empty() {
1317 return Err(self.err(
1318 "CREATE TRIGGER: `UPDATE OF` requires at least one column name".into(),
1319 ));
1320 }
1321 Ok((ev, cols))
1322 }
1323
1324 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
1331 let declarations = if matches!(
1333 self.peek(),
1334 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
1335 ) {
1336 self.advance();
1337 self.parse_plpgsql_declare_block()?
1338 } else {
1339 Vec::new()
1340 };
1341 if !matches!(self.peek(), Token::Begin) {
1346 return Err(self.err(alloc::format!(
1347 "expected BEGIN at start of plpgsql block, got {:?}",
1348 self.peek()
1349 )));
1350 }
1351 self.advance();
1352 let statements = self.parse_plpgsql_stmt_list_until_end()?;
1353 Ok(PlPgSqlBlock {
1354 declarations,
1355 statements,
1356 })
1357 }
1358
1359 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
1363 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
1364 loop {
1365 if matches!(self.peek(), Token::Begin) {
1366 return Ok(out);
1367 }
1368 let name = self.expect_ident_like()?;
1369 let ty_token = self.expect_ident_like()?;
1370 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1371 Some(t) => FunctionArgType::Typed(t),
1372 None => FunctionArgType::Raw(ty_token),
1373 };
1374 let default = match self.peek() {
1375 Token::ColonEq => {
1376 self.advance();
1377 Some(self.parse_expr(0)?)
1378 }
1379 Token::Eq => {
1380 self.advance();
1384 Some(self.parse_expr(0)?)
1385 }
1386 _ => None,
1387 };
1388 if !matches!(self.peek(), Token::Semicolon) {
1390 return Err(self.err(alloc::format!(
1391 "expected ; after DECLARE entry for {name:?}, got {:?}",
1392 self.peek()
1393 )));
1394 }
1395 self.advance();
1396 out.push(PlPgSqlDeclare { name, ty, default });
1397 }
1398 }
1399
1400 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
1405 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
1406 loop {
1407 while matches!(self.peek(), Token::Semicolon) {
1409 self.advance();
1410 }
1411 if matches!(
1413 self.peek(),
1414 Token::Ident(s) | Token::QuotedIdent(s)
1415 if s.eq_ignore_ascii_case("end")
1416 || s.eq_ignore_ascii_case("else")
1417 || s.eq_ignore_ascii_case("elsif")
1418 || s.eq_ignore_ascii_case("elseif")
1419 ) {
1420 return Ok(statements);
1421 }
1422 let stmt = self.parse_plpgsql_stmt()?;
1425 statements.push(stmt);
1426 match self.peek() {
1427 Token::Semicolon => {
1428 self.advance();
1429 }
1430 Token::Ident(s) | Token::QuotedIdent(s)
1431 if s.eq_ignore_ascii_case("end")
1432 || s.eq_ignore_ascii_case("else")
1433 || s.eq_ignore_ascii_case("elsif")
1434 || s.eq_ignore_ascii_case("elseif") =>
1435 {
1436 }
1438 other => {
1439 return Err(self.err(alloc::format!(
1440 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
1441 )));
1442 }
1443 }
1444 }
1445 }
1446
1447 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1448 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
1450 {
1451 self.advance();
1452 return self.parse_plpgsql_return();
1453 }
1454 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
1456 {
1457 self.advance();
1458 return self.parse_plpgsql_if();
1459 }
1460 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
1462 {
1463 self.advance();
1464 return self.parse_plpgsql_raise();
1465 }
1466 if matches!(self.peek(), Token::Insert)
1472 || matches!(self.peek(), Token::Select)
1473 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("delete"))
1474 {
1475 let stmt = self.parse_one_statement()?;
1476 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
1477 }
1478 let target = self.parse_plpgsql_assign_target()?;
1481 match self.peek() {
1484 Token::ColonEq => {
1485 self.advance();
1486 }
1487 Token::Colon => {
1488 self.advance();
1489 if !matches!(self.peek(), Token::Eq) {
1490 return Err(self.err(alloc::format!(
1491 "expected := after plpgsql assign target, got `:` then {:?}",
1492 self.peek()
1493 )));
1494 }
1495 self.advance();
1496 }
1497 other => {
1498 return Err(self.err(alloc::format!(
1499 "expected := after plpgsql assign target, got {other:?}"
1500 )));
1501 }
1502 }
1503 let value = self.parse_expr(0)?;
1504 Ok(PlPgSqlStmt::Assign { target, value })
1505 }
1506
1507 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1510 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
1511 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
1512 loop {
1513 let cond = self.parse_expr(0)?;
1515 let then_kw = self.expect_ident_like()?;
1516 if !then_kw.eq_ignore_ascii_case("then") {
1517 return Err(self.err(alloc::format!(
1518 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
1519 )));
1520 }
1521 let body = self.parse_plpgsql_stmt_list_until_end()?;
1522 branches.push((cond, body));
1523 match self.peek() {
1525 Token::Ident(s) | Token::QuotedIdent(s)
1526 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
1527 {
1528 self.advance();
1529 continue;
1530 }
1531 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
1532 self.advance();
1533 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
1534 break;
1535 }
1536 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
1537 break;
1538 }
1539 other => {
1540 return Err(self.err(alloc::format!(
1541 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
1542 )));
1543 }
1544 }
1545 }
1546 let end_kw = self.expect_ident_like()?;
1549 if !end_kw.eq_ignore_ascii_case("end") {
1550 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
1551 }
1552 let if_kw = self.expect_ident_like()?;
1553 if !if_kw.eq_ignore_ascii_case("if") {
1554 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
1555 }
1556 Ok(PlPgSqlStmt::If {
1557 branches,
1558 else_branch,
1559 })
1560 }
1561
1562 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1566 let lvl_ident = self.expect_ident_like()?;
1567 let level = match lvl_ident.to_ascii_lowercase().as_str() {
1568 "notice" => RaiseLevel::Notice,
1569 "warning" => RaiseLevel::Warning,
1570 "info" => RaiseLevel::Info,
1571 "log" => RaiseLevel::Log,
1572 "debug" => RaiseLevel::Debug,
1573 "exception" => RaiseLevel::Exception,
1574 other => {
1575 return Err(self.err(alloc::format!(
1576 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
1577 )));
1578 }
1579 };
1580 let Token::String(msg) = self.peek() else {
1584 return Err(self.err(alloc::format!(
1585 "expected RAISE message string, got {:?}",
1586 self.peek()
1587 )));
1588 };
1589 let message = msg.clone();
1590 self.advance();
1591 let mut args: Vec<Expr> = Vec::new();
1593 while matches!(self.peek(), Token::Comma) {
1594 self.advance();
1595 args.push(self.parse_expr(0)?);
1596 }
1597 Ok(PlPgSqlStmt::Raise {
1598 level,
1599 message,
1600 args,
1601 })
1602 }
1603
1604 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
1605 let head = match self.advance() {
1619 Token::Ident(s) | Token::QuotedIdent(s) => s,
1620 other => {
1621 return Err(self.err(alloc::format!(
1622 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
1623 )));
1624 }
1625 };
1626 if matches!(self.peek(), Token::Dot) {
1627 self.advance();
1628 let col = self.expect_ident_like()?;
1629 if head.eq_ignore_ascii_case("new") {
1630 return Ok(AssignTarget::NewColumn(col));
1631 }
1632 if head.eq_ignore_ascii_case("old") {
1633 return Ok(AssignTarget::OldColumn(col));
1634 }
1635 return Err(self.err(alloc::format!(
1636 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
1637 got {head:?}.<col>"
1638 )));
1639 }
1640 Ok(AssignTarget::Local(head))
1641 }
1642
1643 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1644 match self.peek() {
1646 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
1647 self.advance();
1648 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
1649 }
1650 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
1651 self.advance();
1652 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
1653 }
1654 Token::Null => {
1655 self.advance();
1656 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1657 }
1658 Token::Semicolon => {
1661 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1662 }
1663 _ => {}
1664 }
1665 let e = self.parse_expr(0)?;
1667 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
1668 }
1669
1670 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
1671 if matches!(self.peek(), Token::Insert) {
1676 self.advance();
1677 return Ok(TriggerEvent::Insert);
1678 }
1679 match self.peek() {
1680 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1681 self.advance();
1682 Ok(TriggerEvent::Update)
1683 }
1684 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1685 self.advance();
1686 Ok(TriggerEvent::Delete)
1687 }
1688 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
1689 self.advance();
1690 Ok(TriggerEvent::Truncate)
1691 }
1692 other => Err(self.err(alloc::format!(
1693 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
1694 ))),
1695 }
1696 }
1697
1698 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
1705 let name = self.expect_ident_or_string()?;
1706 let scope = if matches!(self.peek(), Token::For) {
1709 self.advance();
1710 if matches!(self.peek(), Token::All) {
1711 self.advance();
1712 if !matches!(self.peek(), Token::Tables) {
1713 return Err(self.err(format!(
1714 "expected TABLES after FOR ALL, got {:?}",
1715 self.peek()
1716 )));
1717 }
1718 self.advance();
1719 if matches!(self.peek(), Token::Except) {
1720 self.advance();
1721 let tables = self.parse_publication_table_list()?;
1722 PublicationScope::AllTablesExcept(tables)
1723 } else {
1724 PublicationScope::AllTables
1725 }
1726 } else if matches!(self.peek(), Token::Table | Token::Tables) {
1727 self.advance();
1730 let tables = self.parse_publication_table_list()?;
1731 PublicationScope::ForTables(tables)
1732 } else {
1733 return Err(self.err(format!(
1734 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
1735 self.peek()
1736 )));
1737 }
1738 } else {
1739 PublicationScope::AllTables
1740 };
1741 Ok(Statement::CreatePublication(CreatePublicationStatement {
1742 name,
1743 scope,
1744 }))
1745 }
1746
1747 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
1752 let first = self.expect_ident_like()?;
1753 let mut out = alloc::vec![first];
1754 while matches!(self.peek(), Token::Comma) {
1755 self.advance();
1756 out.push(self.expect_ident_like()?);
1757 }
1758 Ok(out)
1759 }
1760
1761 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
1769 let name = self.expect_ident_or_string()?;
1770 if !matches!(self.peek(), Token::Connection) {
1771 return Err(self.err(format!(
1772 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
1773 self.peek()
1774 )));
1775 }
1776 self.advance();
1777 let conn_str = self.expect_string_literal()?;
1778 if !matches!(self.peek(), Token::Publication) {
1779 return Err(self.err(format!(
1780 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
1781 self.peek()
1782 )));
1783 }
1784 self.advance();
1785 let first = self.expect_ident_like()?;
1788 let mut publications = alloc::vec![first];
1789 while matches!(self.peek(), Token::Comma) {
1790 self.advance();
1791 publications.push(self.expect_ident_like()?);
1792 }
1793 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
1794 name,
1795 conn_str,
1796 publications,
1797 }))
1798 }
1799
1800 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
1807 let mut name = self.expect_ident_like()?;
1808 while matches!(self.peek(), Token::Dot) {
1809 self.advance();
1810 let next = self.expect_ident_like()?;
1811 name.push('.');
1812 name.push_str(&next);
1813 }
1814 Ok(name.to_ascii_lowercase())
1815 }
1816
1817 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
1818 match self.advance() {
1819 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
1820 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
1821 Ok(crate::ast::SetValue::Default)
1822 }
1823 Token::Ident(s) | Token::QuotedIdent(s) => {
1824 let mut accum = s;
1825 while matches!(self.peek(), Token::Dot) {
1826 self.advance();
1827 let next = self.expect_ident_like()?;
1828 accum.push('.');
1829 accum.push_str(&next);
1830 }
1831 Ok(crate::ast::SetValue::Ident(accum))
1832 }
1833 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
1834 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
1835 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
1841 Token::Minus => match self.advance() {
1846 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
1847 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
1848 other => Err(self.err(format!(
1849 "expected numeric after `-` in SET value, got {other:?}"
1850 ))),
1851 },
1852 other => Err(self.err(format!(
1853 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
1854 ))),
1855 }
1856 }
1857
1858 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
1859 if !matches!(self.peek(), Token::For) {
1863 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
1864 }
1865 self.advance();
1866 self.expect_keyword_ident("wal")?;
1867 self.expect_keyword_ident("position")?;
1868 let pos = self.expect_u64_literal()?;
1869 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
1870 {
1871 self.advance();
1872 self.expect_keyword_ident("timeout")?;
1873 Some(self.expect_u64_literal()?)
1874 } else {
1875 None
1876 };
1877 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
1878 }
1879
1880 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
1884 match self.advance() {
1885 Token::Integer(n) if n >= 0 => Ok(n as u64),
1886 Token::Integer(n) => Err(ParseError {
1887 message: format!("expected non-negative integer, got {n}"),
1888 token_pos: self.pos.saturating_sub(1),
1889 }),
1890 other => Err(ParseError {
1891 message: format!("expected integer literal, got {other:?}"),
1892 token_pos: self.pos.saturating_sub(1),
1893 }),
1894 }
1895 }
1896
1897 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
1901 let name = self.expect_ident_or_string()?;
1902 self.expect_keyword_ident("with")?;
1903 self.expect_keyword_ident("password")?;
1904 let password = self.expect_string_literal()?;
1905 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
1906 && s.eq_ignore_ascii_case("role")
1907 {
1908 self.advance();
1909 self.expect_string_literal()?
1910 } else {
1911 "readonly".to_string()
1912 };
1913 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
1914 name,
1915 password,
1916 role,
1917 }))
1918 }
1919
1920 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
1923 let table = self.expect_ident_like()?;
1924 self.expect_keyword_ident("set")?;
1925 let mut assignments = Vec::new();
1926 loop {
1927 let col = self.expect_ident_like()?;
1928 if !matches!(self.peek(), Token::Eq) {
1929 return Err(self.err(format!(
1930 "expected `=` after column name in UPDATE SET, got {:?}",
1931 self.peek()
1932 )));
1933 }
1934 self.advance();
1935 let value = self.parse_expr(0)?;
1936 assignments.push((col, value));
1937 if matches!(self.peek(), Token::Comma) {
1938 self.advance();
1939 continue;
1940 }
1941 break;
1942 }
1943 let where_ = if matches!(self.peek(), Token::Where) {
1944 self.advance();
1945 Some(self.parse_expr(0)?)
1946 } else {
1947 None
1948 };
1949 let returning = self.parse_optional_returning()?;
1950 Ok(Statement::Update(crate::ast::UpdateStatement {
1951 table,
1952 assignments,
1953 where_,
1954 returning,
1955 }))
1956 }
1957
1958 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
1961 if !matches!(self.peek(), Token::From) {
1962 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
1963 }
1964 self.advance();
1965 let table = self.expect_ident_like()?;
1966 let where_ = if matches!(self.peek(), Token::Where) {
1967 self.advance();
1968 Some(self.parse_expr(0)?)
1969 } else {
1970 None
1971 };
1972 let returning = self.parse_optional_returning()?;
1973 Ok(Statement::Delete(crate::ast::DeleteStatement {
1974 table,
1975 where_,
1976 returning,
1977 }))
1978 }
1979
1980 fn parse_optional_returning(
1985 &mut self,
1986 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
1987 let is_returning_kw = matches!(
1988 self.peek(),
1989 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
1990 );
1991 if !is_returning_kw {
1992 return Ok(None);
1993 }
1994 self.advance();
1995 let mut items = Vec::new();
1996 loop {
1997 items.push(self.parse_select_item()?);
1998 if matches!(self.peek(), Token::Comma) {
1999 self.advance();
2000 continue;
2001 }
2002 break;
2003 }
2004 Ok(Some(items))
2005 }
2006
2007 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
2015 match self.advance() {
2022 Token::Index => {}
2023 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
2024 Token::Table => {
2027 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2028 self.advance();
2029 }
2030 return self.parse_alter_table_after_keyword();
2031 }
2032 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
2033 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2034 self.advance();
2035 }
2036 return self.parse_alter_table_after_keyword();
2037 }
2038 Token::Ident(s) | Token::QuotedIdent(s)
2044 if matches!(
2045 s.to_ascii_lowercase().as_str(),
2046 "sequence"
2047 | "view"
2048 | "function"
2049 | "type"
2050 | "domain"
2051 | "database"
2052 | "role"
2053 | "schema"
2054 | "owner"
2055 | "default"
2056 | "extension"
2057 | "materialized"
2058 | "policy"
2059 | "publication"
2060 | "subscription"
2061 ) =>
2062 {
2063 self.consume_until_statement_boundary();
2064 return Ok(Statement::Empty);
2065 }
2066 other => {
2067 return Err(self.err(format!(
2068 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
2069 after ALTER, got {other:?}"
2070 )));
2071 }
2072 }
2073 let name = self.expect_ident_like()?;
2074 self.expect_keyword_ident("rebuild")?;
2076 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2078 self.advance();
2079 if !matches!(self.peek(), Token::LParen) {
2080 return Err(self.err(format!(
2081 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
2082 self.peek()
2083 )));
2084 }
2085 self.advance();
2086 self.expect_keyword_ident("encoding")?;
2087 if !matches!(self.peek(), Token::Eq) {
2088 return Err(self.err(format!(
2089 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
2090 self.peek()
2091 )));
2092 }
2093 self.advance();
2094 let enc_ident = match self.advance() {
2095 Token::Ident(s) | Token::QuotedIdent(s) => s,
2096 other => {
2097 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
2098 }
2099 };
2100 let enc = match enc_ident.to_ascii_lowercase().as_str() {
2101 "f32" => VecEncoding::F32,
2102 "sq8" => VecEncoding::Sq8,
2103 "half" => VecEncoding::F16,
2104 other => {
2105 return Err(self.err(format!(
2106 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
2107 )));
2108 }
2109 };
2110 if !matches!(self.peek(), Token::RParen) {
2111 return Err(self.err(format!(
2112 "expected ')' after encoding value, got {:?}",
2113 self.peek()
2114 )));
2115 }
2116 self.advance();
2117 Some(enc)
2118 } else {
2119 None
2120 };
2121 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
2122 name,
2123 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
2124 }))
2125 }
2126
2127 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
2133 let table_name = self.expect_ident_like()?;
2134 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
2135 loop {
2136 let subaction = self.parse_alter_table_subaction()?;
2137 targets.extend(subaction);
2141 if matches!(self.peek(), Token::Comma) {
2142 self.advance();
2143 continue;
2144 }
2145 break;
2146 }
2147 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
2148 name: table_name,
2149 targets,
2150 }))
2151 }
2152
2153 fn parse_alter_table_subaction(
2157 &mut self,
2158 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
2159 match self.peek() {
2160 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2161 self.advance();
2162 let setting = self.expect_ident_like()?;
2163 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
2164 return Err(self.err(alloc::format!(
2165 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
2166 )));
2167 }
2168 if !matches!(self.peek(), Token::Eq) {
2169 return Err(self.err(alloc::format!(
2170 "expected '=' after hot_tier_bytes, got {:?}",
2171 self.peek()
2172 )));
2173 }
2174 self.advance();
2175 let n = self.expect_u64_literal()?;
2176 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
2177 }
2178 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
2179 self.advance();
2180 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
2187 {
2188 let kind_pos = self.pos + 2;
2191 let kind = self.tokens.get(kind_pos).cloned();
2192 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
2193 {
2194 let fk = self.parse_table_level_fk()?;
2195 return Ok(alloc::vec![
2196 crate::ast::AlterTableTarget::AddForeignKey(fk)
2197 ]);
2198 }
2199 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
2200 {
2201 self.advance(); let _name = self.expect_ident_like()?;
2203 self.advance(); self.expect_keyword_ident("key")?;
2205 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2206 return Ok(alloc::vec![
2207 crate::ast::AlterTableTarget::AddTableConstraint(
2208 crate::ast::TableConstraint::PrimaryKey {
2209 name: None,
2210 columns: cols,
2211 }
2212 )
2213 ]);
2214 }
2215 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
2216 {
2217 self.advance(); let _name = self.expect_ident_like()?;
2219 self.advance(); let cols = self.parse_paren_ident_list("UNIQUE")?;
2221 return Ok(alloc::vec![
2222 crate::ast::AlterTableTarget::AddTableConstraint(
2223 crate::ast::TableConstraint::Unique {
2224 name: None,
2225 columns: cols,
2226 nulls_not_distinct: false,
2227 }
2228 )
2229 ]);
2230 }
2231 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
2232 {
2233 self.advance(); let _name = self.expect_ident_like()?;
2235 self.advance(); if !matches!(self.peek(), Token::LParen) {
2237 return Err(self.err(alloc::format!(
2238 "expected '(' after CHECK, got {:?}", self.peek()
2239 )));
2240 }
2241 self.advance();
2242 let expr = self.parse_expr(0)?;
2243 if matches!(self.peek(), Token::RParen) {
2244 self.advance();
2245 }
2246 return Ok(alloc::vec![
2247 crate::ast::AlterTableTarget::AddTableConstraint(
2248 crate::ast::TableConstraint::Check { name: None, expr }
2249 )
2250 ]);
2251 }
2252 }
2255 let is_fk = matches!(
2256 self.peek(),
2257 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2258 || s.eq_ignore_ascii_case("foreign")
2259 );
2260 if is_fk {
2261 let fk = self.parse_table_level_fk()?;
2262 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
2263 }
2264 match self.peek().clone() {
2267 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
2268 self.advance();
2269 self.expect_keyword_ident("key")?;
2270 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2271 return Ok(alloc::vec![
2272 crate::ast::AlterTableTarget::AddTableConstraint(
2273 crate::ast::TableConstraint::PrimaryKey {
2274 name: None,
2275 columns: cols,
2276 }
2277 )
2278 ]);
2279 }
2280 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
2281 self.advance();
2282 let cols = self.parse_paren_ident_list("UNIQUE")?;
2283 return Ok(alloc::vec![
2284 crate::ast::AlterTableTarget::AddTableConstraint(
2285 crate::ast::TableConstraint::Unique {
2286 name: None,
2287 columns: cols,
2288 nulls_not_distinct: false,
2289 }
2290 )
2291 ]);
2292 }
2293 _ => {}
2294 }
2295 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2296 self.advance();
2297 }
2298 let mut if_not_exists = false;
2299 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2300 self.advance();
2301 if !matches!(self.peek(), Token::Not) {
2302 return Err(self.err(alloc::format!(
2303 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
2304 self.peek()
2305 )));
2306 }
2307 self.advance();
2308 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
2309 return Err(self.err(alloc::format!(
2310 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
2311 self.peek()
2312 )));
2313 }
2314 self.advance();
2315 if_not_exists = true;
2316 }
2317 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
2321 let col_name = column.name.clone();
2322 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
2323 column,
2324 if_not_exists,
2325 }];
2326 if let Some(mut fk) = col_level_fk {
2327 if fk.columns.is_empty() {
2328 fk.columns.push(col_name);
2329 }
2330 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
2331 }
2332 Ok(out)
2333 }
2334 Token::Drop => {
2335 self.advance();
2336 let subject = match self.peek() {
2343 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
2344 self.advance();
2345 "constraint"
2346 }
2347 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
2348 self.advance();
2349 "column"
2350 }
2351 Token::Ident(_) | Token::QuotedIdent(_) => "column",
2355 other => {
2356 return Err(self.err(alloc::format!(
2357 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
2358 )));
2359 }
2360 };
2361 let mut if_exists = false;
2362 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2363 let n1 = self.tokens.get(self.pos + 1);
2364 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
2365 self.advance();
2366 self.advance();
2367 if_exists = true;
2368 }
2369 }
2370 let name = self.expect_ident_like()?;
2371 let mut cascade = false;
2372 if matches!(
2373 self.peek(),
2374 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
2375 || s.eq_ignore_ascii_case("restrict")
2376 ) {
2377 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
2378 {
2379 cascade = true;
2380 }
2381 self.advance();
2382 }
2383 if subject == "constraint" {
2384 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
2385 name,
2386 if_exists,
2387 }])
2388 } else {
2389 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
2390 column: name,
2391 if_exists,
2392 cascade,
2393 }])
2394 }
2395 }
2396 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
2397 self.advance();
2398 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2399 self.advance();
2400 }
2401 let col_name = self.expect_ident_like()?;
2402 match self.peek() {
2403 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
2404 self.advance();
2405 }
2406 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2414 self.consume_until_statement_boundary();
2419 return Ok(Vec::new());
2420 }
2421 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
2422 self.consume_until_statement_boundary();
2424 return Ok(Vec::new());
2425 }
2426 other => {
2427 return Err(self.err(alloc::format!(
2428 "expected TYPE / SET / DROP after ALTER COLUMN <name>, got {other:?}"
2429 )));
2430 }
2431 }
2432 let new_type = self.parse_column_type_name()?;
2433 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
2434 {
2435 self.advance();
2436 Some(self.parse_expr(0)?)
2437 } else {
2438 None
2439 };
2440 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
2441 column: col_name,
2442 new_type,
2443 using,
2444 }])
2445 }
2446 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
2453 self.advance();
2454 if matches!(self.peek(), Token::To)
2459 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
2460 {
2461 return Err(self.err(alloc::format!(
2462 "ALTER TABLE RENAME TO <new_name> (table rename) is not supported; \
2463 use RENAME COLUMN <old> TO <new> instead"
2464 )));
2465 }
2466 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2467 self.advance();
2468 }
2469 let old = self.expect_ident_like()?;
2470 if matches!(self.peek(), Token::To) {
2473 self.advance();
2474 } else {
2475 self.expect_keyword_ident("to")?;
2476 }
2477 let new = self.expect_ident_like()?;
2478 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
2479 old,
2480 new,
2481 }])
2482 }
2483 Token::Ident(s)
2490 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
2491 {
2492 let enabled = s.eq_ignore_ascii_case("enable");
2493 self.advance();
2494 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
2500 return Err(self.err(alloc::format!(
2501 "expected TRIGGER after {}, got {:?}",
2502 if enabled { "ENABLE" } else { "DISABLE" },
2503 self.peek()
2504 )));
2505 }
2506 self.advance();
2507 let which = if matches!(self.peek(), Token::All)
2510 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
2511 {
2512 self.advance();
2513 crate::ast::TriggerSelector::All
2514 } else {
2515 let name = self.expect_ident_like()?;
2516 crate::ast::TriggerSelector::Named(name)
2517 };
2518 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
2519 which,
2520 enabled,
2521 }])
2522 }
2523 other => Err(self.err(alloc::format!(
2524 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
2525 ))),
2526 }
2527 }
2528
2529 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
2531 match self.advance() {
2532 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
2533 other => Err(ParseError {
2534 message: format!("expected {kw:?}, got {other:?}"),
2535 token_pos: self.pos.saturating_sub(1),
2536 }),
2537 }
2538 }
2539
2540 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
2544 match self.advance() {
2545 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
2546 other => Err(ParseError {
2547 message: format!("expected identifier or string, got {other:?}"),
2548 token_pos: self.pos.saturating_sub(1),
2549 }),
2550 }
2551 }
2552
2553 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
2554 match self.advance() {
2555 Token::String(s) => Ok(s),
2556 other => Err(ParseError {
2557 message: format!("expected quoted string, got {other:?}"),
2558 token_pos: self.pos.saturating_sub(1),
2559 }),
2560 }
2561 }
2562
2563 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
2564 let mut head = self.parse_bare_select()?;
2569 while matches!(self.peek(), Token::Union) {
2570 self.advance();
2571 let kind = if matches!(self.peek(), Token::All) {
2572 self.advance();
2573 UnionKind::All
2574 } else {
2575 UnionKind::Distinct
2576 };
2577 let peer = self.parse_bare_select()?;
2578 head.unions.push((kind, peer));
2579 }
2580 head.order_by = if matches!(self.peek(), Token::Order) {
2581 self.advance();
2582 if !matches!(self.peek(), Token::By) {
2583 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
2584 }
2585 self.advance();
2586 let mut keys = Vec::new();
2589 loop {
2590 let expr = self.parse_expr(0)?;
2591 let desc = if matches!(self.peek(), Token::Desc) {
2592 self.advance();
2593 true
2594 } else if matches!(self.peek(), Token::Asc) {
2595 self.advance();
2596 false
2597 } else {
2598 false
2599 };
2600 keys.push(OrderBy { expr, desc });
2601 if matches!(self.peek(), Token::Comma) {
2602 self.advance();
2603 } else {
2604 break;
2605 }
2606 }
2607 keys
2608 } else {
2609 Vec::new()
2610 };
2611 head.limit = if matches!(self.peek(), Token::Limit) {
2612 self.advance();
2613 Some(self.parse_limit_expr("LIMIT")?)
2614 } else {
2615 None
2616 };
2617 head.offset = if matches!(self.peek(), Token::Offset) {
2618 self.advance();
2619 Some(self.parse_limit_expr("OFFSET")?)
2620 } else {
2621 None
2622 };
2623 Ok(Statement::Select(head))
2624 }
2625
2626 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2631 match self.advance() {
2632 Token::Integer(n) if n >= 0 => u32::try_from(n)
2633 .map(crate::ast::LimitExpr::Literal)
2634 .map_err(|_| ParseError {
2635 message: alloc::format!("{label} value too large: {n}"),
2636 token_pos: self.pos.saturating_sub(1),
2637 }),
2638 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2639 other => Err(ParseError {
2640 message: alloc::format!(
2641 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2642 ),
2643 token_pos: self.pos.saturating_sub(1),
2644 }),
2645 }
2646 }
2647
2648 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2653 if !matches!(self.peek(), Token::Select) {
2654 return Err(self.err(format!(
2655 "expected SELECT to start a query block, got {:?}",
2656 self.peek()
2657 )));
2658 }
2659 self.advance();
2660 let distinct = if matches!(self.peek(), Token::Distinct) {
2661 self.advance();
2662 true
2663 } else {
2664 false
2665 };
2666 let items = self.parse_select_list()?;
2667 let from = if matches!(self.peek(), Token::From) {
2668 self.advance();
2669 Some(self.parse_from_clause()?)
2670 } else {
2671 None
2672 };
2673 let where_ = if matches!(self.peek(), Token::Where) {
2674 self.advance();
2675 Some(self.parse_expr(0)?)
2676 } else {
2677 None
2678 };
2679 let mut group_by_all = false;
2680 let group_by = if matches!(self.peek(), Token::Group) {
2681 self.advance();
2682 if !matches!(self.peek(), Token::By) {
2683 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2684 }
2685 self.advance();
2686 if matches!(self.peek(), Token::All) {
2689 self.advance();
2690 group_by_all = true;
2691 None
2692 } else {
2693 let mut groups = Vec::new();
2694 loop {
2695 groups.push(self.parse_expr(0)?);
2696 if matches!(self.peek(), Token::Comma) {
2697 self.advance();
2698 } else {
2699 break;
2700 }
2701 }
2702 Some(groups)
2703 }
2704 } else {
2705 None
2706 };
2707 let having = if matches!(self.peek(), Token::Having) {
2708 self.advance();
2709 Some(self.parse_expr(0)?)
2710 } else {
2711 None
2712 };
2713 Ok(SelectStatement {
2714 ctes: Vec::new(),
2715 distinct,
2716 items,
2717 from,
2718 where_,
2719 group_by,
2720 group_by_all,
2721 having,
2722 unions: Vec::new(),
2723 order_by: Vec::new(),
2724 limit: None,
2725 offset: None,
2726 })
2727 }
2728
2729 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2730 debug_assert!(matches!(self.peek(), Token::Table));
2732 self.advance();
2733 let if_not_exists = self.consume_if_not_exists();
2734 let name = self.expect_ident_like()?;
2735 if !matches!(self.peek(), Token::LParen) {
2736 return Err(self.err(format!(
2737 "expected '(' after table name, got {:?}",
2738 self.peek()
2739 )));
2740 }
2741 self.advance();
2742 let mut columns = Vec::new();
2743 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
2744 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
2745 loop {
2746 if self.peek_table_level_pk_start() {
2752 table_constraints.push(self.parse_table_level_primary_key()?);
2753 } else if self.peek_table_level_unique_start() {
2754 table_constraints.push(self.parse_table_level_unique()?);
2755 } else if self.peek_table_level_check_start() {
2756 table_constraints.push(self.parse_table_level_check()?);
2758 } else if self.peek_mysql_inline_key_start() {
2759 if let Some(uc) = self.parse_mysql_inline_key()? {
2765 table_constraints.push(uc);
2766 }
2767 } else if self.peek_constraint_or_fk_start() {
2768 foreign_keys.push(self.parse_table_level_fk()?);
2769 } else {
2770 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
2771 if col.is_unique {
2775 table_constraints.push(crate::ast::TableConstraint::Unique {
2776 name: None,
2777 columns: alloc::vec![col.name.clone()],
2778 nulls_not_distinct: false,
2779 });
2780 }
2781 if let Some(check_expr) = col.check.clone() {
2782 table_constraints.push(crate::ast::TableConstraint::Check {
2783 name: None,
2784 expr: check_expr,
2785 });
2786 }
2787 columns.push(col);
2788 if let Some(fk) = col_level_fk {
2789 foreign_keys.push(fk);
2790 }
2791 }
2792 match self.peek() {
2793 Token::Comma => {
2794 self.advance();
2795 }
2796 Token::RParen => {
2797 self.advance();
2798 break;
2799 }
2800 other => {
2801 return Err(
2802 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2803 );
2804 }
2805 }
2806 }
2807 if columns.is_empty() {
2808 return Err(self.err("CREATE TABLE requires at least one column".into()));
2809 }
2810 self.consume_mysql_table_options();
2817 Ok(Statement::CreateTable(CreateTableStatement {
2818 name,
2819 columns,
2820 if_not_exists,
2821 foreign_keys,
2822 table_constraints,
2823 }))
2824 }
2825
2826 fn peek_mysql_inline_key_start(&self) -> bool {
2835 let cur = self.peek();
2836 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
2846 match self.tokens.get(skip) {
2849 Some(Token::LParen) => true,
2850 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
2851 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
2852 }
2853 _ => false,
2854 }
2855 };
2856 let is_key_or_index_tok = |t: &Token| -> bool {
2860 matches!(t, Token::Index)
2861 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
2862 };
2863 match cur {
2864 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
2865 Token::Ident(s)
2866 if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") =>
2867 {
2868 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
2869 }
2870 Token::Ident(s)
2871 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
2872 {
2873 let nxt = self.tokens.get(self.pos + 1);
2874 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
2875 self.pos + 2
2876 } else {
2877 self.pos + 1
2878 };
2879 after_keyword_followed_by_paren_or_ident_paren(after_after)
2880 }
2881 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
2882 let nxt = self.tokens.get(self.pos + 1);
2883 if !nxt.is_some_and(is_key_or_index_tok) {
2884 return false;
2885 }
2886 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
2887 }
2888 _ => false,
2889 }
2890 }
2891
2892 fn parse_mysql_inline_key(
2901 &mut self,
2902 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
2903 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
2905 {
2906 self.advance();
2907 true
2908 } else {
2909 false
2910 };
2911 let is_fulltext_or_spatial = if matches!(
2916 self.peek(),
2917 Token::Ident(s) if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial")
2918 ) {
2919 self.advance();
2920 true
2921 } else {
2922 false
2923 };
2924 match self.peek() {
2927 Token::Index => {
2928 self.advance();
2929 }
2930 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
2931 self.advance();
2932 }
2933 other => {
2934 return Err(self.err(alloc::format!(
2935 "expected KEY/INDEX in inline index declaration, got {other:?}"
2936 )));
2937 }
2938 }
2939 let mut idx_name: Option<String> = None;
2944 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
2945 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
2946 {
2947 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
2948 idx_name = Some(s);
2949 }
2950 }
2951 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2953 self.advance();
2954 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2955 self.advance();
2956 }
2957 }
2958 if !matches!(self.peek(), Token::LParen) {
2960 return Err(self.err(alloc::format!(
2961 "expected '(' in inline KEY/INDEX, got {:?}",
2962 self.peek()
2963 )));
2964 }
2965 self.advance();
2966 let mut cols: Vec<String> = Vec::new();
2967 loop {
2968 match self.peek().clone() {
2969 Token::Ident(s) | Token::QuotedIdent(s) => {
2970 self.advance();
2971 cols.push(s);
2972 }
2973 _ => break,
2974 }
2975 if matches!(self.peek(), Token::LParen) {
2977 let mut depth = 1usize;
2978 self.advance();
2979 while depth > 0 {
2980 match self.peek() {
2981 Token::LParen => depth += 1,
2982 Token::RParen => depth -= 1,
2983 Token::Eof => break,
2984 _ => {}
2985 }
2986 self.advance();
2987 }
2988 }
2989 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
2991 || matches!(self.peek(), Token::Asc | Token::Desc)
2992 {
2993 self.advance();
2994 }
2995 if matches!(self.peek(), Token::Comma) {
2996 self.advance();
2997 continue;
2998 }
2999 break;
3000 }
3001 if matches!(self.peek(), Token::RParen) {
3002 self.advance();
3003 }
3004 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
3007 self.advance();
3008 }
3009 if cols.is_empty() {
3010 return Ok(None);
3011 }
3012 if is_unique {
3013 Ok(Some(crate::ast::TableConstraint::Unique {
3019 name: idx_name,
3020 columns: cols,
3021 nulls_not_distinct: false,
3022 }))
3023 } else if is_fulltext_or_spatial {
3024 Ok(None)
3026 } else {
3027 Ok(Some(crate::ast::TableConstraint::Index {
3030 name: idx_name,
3031 columns: cols,
3032 }))
3033 }
3034 }
3035
3036 fn consume_mysql_table_options(&mut self) {
3041 loop {
3042 let name_lc = match self.peek().clone() {
3046 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
3047 Token::Default => alloc::string::String::from("default"),
3048 _ => break,
3049 };
3050 let known = matches!(
3051 name_lc.as_str(),
3052 "engine"
3053 | "default"
3054 | "charset"
3055 | "collate"
3056 | "auto_increment"
3057 | "row_format"
3058 | "comment"
3059 | "pack_keys"
3060 | "stats_persistent"
3061 | "stats_auto_recalc"
3062 | "stats_sample_pages"
3063 | "key_block_size"
3064 | "tablespace"
3065 | "min_rows"
3066 | "max_rows"
3067 | "checksum"
3068 | "delay_key_write"
3069 | "insert_method"
3070 | "data"
3071 | "index"
3072 | "encryption"
3073 | "compression"
3074 );
3075 if !known {
3076 break;
3077 }
3078 self.advance(); if name_lc == "default" {
3082 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
3083 self.advance();
3084 }
3085 }
3086 if matches!(self.peek(), Token::Eq) {
3087 self.advance();
3088 }
3089 match self.peek() {
3090 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
3091 self.advance();
3092 }
3093 _ => {}
3094 }
3095 }
3096 }
3097
3098 fn peek_table_level_pk_start(&self) -> bool {
3103 let cur = self.peek();
3104 let nxt = self.tokens.get(self.pos + 1);
3105 let nxt2 = self.tokens.get(self.pos + 2);
3106 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
3107 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
3108 let is_lparen = matches!(nxt2, Some(Token::LParen));
3109 is_primary && is_key && is_lparen
3110 }
3111
3112 fn peek_table_level_unique_start(&self) -> bool {
3116 let cur = self.peek();
3117 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
3118 if !is_unique {
3119 return false;
3120 }
3121 let n1 = self.tokens.get(self.pos + 1);
3122 if matches!(n1, Some(Token::LParen)) {
3124 return true;
3125 }
3126 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
3128 if !is_nulls {
3129 return false;
3130 }
3131 let n2 = self.tokens.get(self.pos + 2);
3132 let n3 = self.tokens.get(self.pos + 3);
3133 let n4 = self.tokens.get(self.pos + 4);
3134 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
3136 return true;
3137 }
3138 if matches!(n2, Some(Token::Not))
3140 && matches!(n3, Some(Token::Distinct))
3141 && matches!(n4, Some(Token::LParen))
3142 {
3143 return true;
3144 }
3145 false
3146 }
3147
3148 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3149 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
3152 Ok(crate::ast::TableConstraint::PrimaryKey {
3153 name: None,
3154 columns,
3155 })
3156 }
3157
3158 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3159 self.advance(); let mut nulls_not_distinct = false;
3164 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
3165 let n1 = self.tokens.get(self.pos + 1);
3166 let n2 = self.tokens.get(self.pos + 2);
3167 let is_not = matches!(n1, Some(Token::Not));
3168 let is_distinct = matches!(n2, Some(Token::Distinct));
3169 if is_not && is_distinct {
3170 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
3174 } else if matches!(n1, Some(Token::Distinct)) {
3175 self.advance(); self.advance(); }
3178 }
3179 let columns = self.parse_paren_ident_list("UNIQUE")?;
3180 Ok(crate::ast::TableConstraint::Unique {
3181 name: None,
3182 columns,
3183 nulls_not_distinct,
3184 })
3185 }
3186
3187 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3191 self.advance(); if !matches!(self.peek(), Token::LParen) {
3193 return Err(self.err(alloc::format!(
3194 "expected '(' after CHECK, got {:?}",
3195 self.peek()
3196 )));
3197 }
3198 self.advance();
3199 let expr = self.parse_expr(0)?;
3200 if !matches!(self.peek(), Token::RParen) {
3201 return Err(self.err(alloc::format!(
3202 "expected ')' to close CHECK predicate, got {:?}",
3203 self.peek()
3204 )));
3205 }
3206 self.advance();
3207 Ok(crate::ast::TableConstraint::Check { name: None, expr })
3208 }
3209
3210 fn peek_table_level_check_start(&self) -> bool {
3212 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
3213 }
3214
3215 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
3216 if !matches!(self.peek(), Token::LParen) {
3217 return Err(self.err(alloc::format!(
3218 "expected '(' after {ctx}, got {:?}",
3219 self.peek()
3220 )));
3221 }
3222 self.advance();
3223 let mut out = Vec::new();
3224 loop {
3225 out.push(self.expect_ident_like()?);
3226 match self.peek() {
3227 Token::Comma => {
3228 self.advance();
3229 }
3230 Token::RParen => {
3231 self.advance();
3232 break;
3233 }
3234 other => {
3235 return Err(self.err(alloc::format!(
3236 "expected ',' or ')' in {ctx} list, got {other:?}"
3237 )));
3238 }
3239 }
3240 }
3241 if out.is_empty() {
3242 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
3243 }
3244 Ok(out)
3245 }
3246
3247 fn peek_constraint_or_fk_start(&self) -> bool {
3252 let is_constraint_kw = matches!(
3253 self.peek(),
3254 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3255 );
3256 let is_foreign_kw = matches!(
3257 self.peek(),
3258 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
3259 );
3260 is_constraint_kw || is_foreign_kw
3261 }
3262
3263 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
3267 let mut name: Option<String> = None;
3268 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
3269 self.advance();
3270 name = Some(self.expect_ident_like()?);
3271 }
3272 match self.advance() {
3274 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
3275 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
3276 }
3277 match self.advance() {
3279 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
3280 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
3281 }
3282 if !matches!(self.peek(), Token::LParen) {
3284 return Err(self.err(format!(
3285 "expected '(' after FOREIGN KEY, got {:?}",
3286 self.peek()
3287 )));
3288 }
3289 self.advance();
3290 let mut columns = Vec::new();
3291 loop {
3292 columns.push(self.expect_ident_like()?);
3293 match self.peek() {
3294 Token::Comma => {
3295 self.advance();
3296 }
3297 Token::RParen => {
3298 self.advance();
3299 break;
3300 }
3301 other => {
3302 return Err(self.err(format!(
3303 "expected ',' or ')' in FK column list, got {other:?}"
3304 )));
3305 }
3306 }
3307 }
3308 if columns.is_empty() {
3309 return Err(self.err("FOREIGN KEY requires at least one column".into()));
3310 }
3311 let (parent_table, parent_columns, on_delete, on_update) =
3312 self.parse_references_tail(columns.len())?;
3313 Ok(ForeignKeyConstraint {
3314 name,
3315 columns,
3316 parent_table,
3317 parent_columns,
3318 on_delete,
3319 on_update,
3320 })
3321 }
3322
3323 fn parse_references_tail(
3328 &mut self,
3329 expected_arity: usize,
3330 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
3331 match self.advance() {
3332 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
3333 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
3334 }
3335 let parent_table = self.expect_ident_like()?;
3336 let mut parent_columns: Vec<String> = Vec::new();
3337 if matches!(self.peek(), Token::LParen) {
3338 self.advance();
3339 loop {
3340 parent_columns.push(self.expect_ident_like()?);
3341 match self.peek() {
3342 Token::Comma => {
3343 self.advance();
3344 }
3345 Token::RParen => {
3346 self.advance();
3347 break;
3348 }
3349 other => {
3350 return Err(self.err(format!(
3351 "expected ',' or ')' in REFERENCES column list, got {other:?}"
3352 )));
3353 }
3354 }
3355 }
3356 }
3357 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
3358 return Err(self.err(format!(
3359 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
3360 expected_arity,
3361 parent_columns.len()
3362 )));
3363 }
3364 loop {
3370 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
3371 return Err(self.err(
3372 "DEFERRABLE constraints are not supported (SPG is single-writer; \
3373 constraints are always evaluated immediately at commit)"
3374 .into(),
3375 ));
3376 }
3377 if matches!(self.peek(), Token::Not) {
3378 let look = self.tokens.get(self.pos + 1);
3379 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
3380 self.advance();
3383 self.advance();
3384 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
3386 {
3387 self.advance();
3388 match self.advance() {
3389 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
3390 other => {
3391 return Err(self.err(format!(
3392 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
3393 got {other:?}"
3394 )));
3395 }
3396 }
3397 }
3398 continue;
3399 }
3400 break;
3401 }
3402 break;
3403 }
3404 let mut on_delete = FkAction::Restrict;
3407 let mut on_update = FkAction::Restrict;
3408 let mut seen_on_delete = false;
3409 let mut seen_on_update = false;
3410 loop {
3411 if !matches!(self.peek(), Token::On) {
3412 break;
3413 }
3414 self.advance();
3415 let which = self.advance();
3416 let action = self.parse_fk_action()?;
3417 match which {
3418 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
3419 if seen_on_delete {
3420 return Err(self.err("ON DELETE specified twice".into()));
3421 }
3422 seen_on_delete = true;
3423 on_delete = action;
3424 }
3425 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
3426 if seen_on_update {
3427 return Err(self.err("ON UPDATE specified twice".into()));
3428 }
3429 seen_on_update = true;
3430 on_update = action;
3431 }
3432 other => {
3433 return Err(
3434 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
3435 );
3436 }
3437 }
3438 }
3439 Ok((parent_table, parent_columns, on_delete, on_update))
3440 }
3441
3442 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
3445 match self.advance() {
3446 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
3447 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
3448 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
3449 Token::Null => Ok(FkAction::SetNull),
3450 Token::Default => Ok(FkAction::SetDefault),
3451 other => Err(self.err(format!(
3452 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
3453 ))),
3454 },
3455 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
3456 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
3457 other => Err(self.err(format!(
3458 "expected ACTION after NO in FK action, got {other:?}"
3459 ))),
3460 },
3461 other => Err(self.err(format!(
3462 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
3463 ))),
3464 }
3465 }
3466
3467 fn consume_if_not_exists(&mut self) -> bool {
3470 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3474 if !looks_like_if {
3475 return false;
3476 }
3477 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
3480 return false;
3481 }
3482 if !matches!(
3483 self.tokens.get(self.pos + 2),
3484 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3485 ) {
3486 return false;
3487 }
3488 self.advance(); self.advance(); self.advance(); true
3492 }
3493
3494 fn consume_if_exists(&mut self) -> bool {
3498 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3499 if !looks_like_if {
3500 return false;
3501 }
3502 if !matches!(
3503 self.tokens.get(self.pos + 1),
3504 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3505 ) {
3506 return false;
3507 }
3508 self.advance(); self.advance(); true
3511 }
3512
3513 fn consume_optional_index_column_qualifiers(&mut self) {
3519 loop {
3520 match self.peek() {
3521 Token::Asc | Token::Desc => {
3522 self.advance();
3523 }
3524 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
3525 let look = self.tokens.get(self.pos + 1);
3526 if matches!(
3527 look,
3528 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
3529 || k.eq_ignore_ascii_case("last")
3530 ) {
3531 self.advance();
3532 self.advance();
3533 } else {
3534 break;
3535 }
3536 }
3537 _ => break,
3538 }
3539 }
3540 }
3541
3542 fn parse_create_index_stmt_after_create(
3543 &mut self,
3544 is_unique: bool,
3545 ) -> Result<Statement, ParseError> {
3546 debug_assert!(matches!(self.peek(), Token::Index));
3548 self.advance();
3549 let if_not_exists = self.consume_if_not_exists();
3550 let name = self.expect_ident_like()?;
3551 if !matches!(self.peek(), Token::On) {
3552 return Err(self.err(format!(
3553 "expected ON after CREATE INDEX <name>, got {:?}",
3554 self.peek()
3555 )));
3556 }
3557 self.advance();
3558 let table = self.expect_ident_like()?;
3559 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3564 self.advance();
3565 let m = self.expect_ident_like()?;
3566 match m.to_ascii_lowercase().as_str() {
3567 "hnsw" => IndexMethod::Hnsw,
3568 "btree" => IndexMethod::BTree,
3569 "brin" => IndexMethod::Brin,
3570 "gin" => IndexMethod::Gin,
3575 "gist" | "spgist" | "hash" => IndexMethod::BTree,
3584 "ivfflat" => IndexMethod::Hnsw,
3592 other => {
3593 return Err(self.err(alloc::format!(
3594 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
3595 )));
3596 }
3597 }
3598 } else {
3599 IndexMethod::BTree
3600 };
3601 if !matches!(self.peek(), Token::LParen) {
3602 return Err(self.err(format!(
3603 "expected '(' before indexed column, got {:?}",
3604 self.peek()
3605 )));
3606 }
3607 self.advance();
3608 let mut opclass: Option<String> = None;
3617 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
3618 Token::Ident(s) | Token::QuotedIdent(s)
3625 if matches!(
3626 self.tokens.get(self.pos + 1),
3627 Some(Token::RParen | Token::Comma)
3628 ) =>
3629 {
3630 self.advance();
3631 (s, None)
3632 }
3633 Token::Ident(s) | Token::QuotedIdent(s)
3643 if matches!(
3644 self.tokens.get(self.pos + 1),
3645 Some(Token::Ident(op) | Token::QuotedIdent(op))
3646 if is_vector_opclass_name(op)
3647 ) =>
3648 {
3649 self.advance(); let op_tok = self.advance();
3653 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
3654 opclass = Some(op.to_ascii_lowercase());
3655 }
3656 (s, None)
3657 }
3658 Token::Ident(_) | Token::QuotedIdent(_) => {
3659 let key_expr = self.parse_expr(0)?;
3660 let primary = extract_first_column(&key_expr).ok_or_else(|| {
3661 self.err("expression index key must reference at least one column".into())
3662 })?;
3663 (primary, Some(key_expr))
3664 }
3665 other => {
3666 return Err(self.err(format!(
3667 "expected column ident or expression, got {other:?}"
3668 )));
3669 }
3670 };
3671 let mut extra_columns: Vec<String> = Vec::new();
3680 self.consume_optional_index_column_qualifiers();
3682 while matches!(self.peek(), Token::Comma) {
3683 self.advance();
3684 let extra = self.expect_ident_like()?;
3685 self.consume_optional_index_column_qualifiers();
3686 extra_columns.push(extra);
3687 }
3688 if !matches!(self.peek(), Token::RParen) {
3689 return Err(self.err(format!(
3690 "expected ')' after indexed column / expression, got {:?}",
3691 self.peek()
3692 )));
3693 }
3694 self.advance();
3695 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
3699 {
3700 self.advance();
3701 if !matches!(self.peek(), Token::LParen) {
3702 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
3703 }
3704 self.advance();
3705 let mut cols = Vec::new();
3706 loop {
3707 cols.push(self.expect_ident_like()?);
3708 match self.peek() {
3709 Token::Comma => {
3710 self.advance();
3711 }
3712 Token::RParen => {
3713 self.advance();
3714 break;
3715 }
3716 other => {
3717 return Err(self.err(format!(
3718 "expected ',' or ')' in INCLUDE list, got {other:?}"
3719 )));
3720 }
3721 }
3722 }
3723 cols
3724 } else {
3725 Vec::new()
3726 };
3727 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3733 self.advance();
3734 if !matches!(self.peek(), Token::LParen) {
3735 return Err(self.err(format!(
3736 "expected '(' after WITH in CREATE INDEX, got {:?}",
3737 self.peek()
3738 )));
3739 }
3740 self.advance();
3741 loop {
3742 if matches!(self.peek(), Token::RParen) {
3743 self.advance();
3744 break;
3745 }
3746 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
3749 self.advance();
3750 let _ = self.advance(); }
3752 match self.peek() {
3753 Token::Comma => {
3754 self.advance();
3755 }
3756 Token::RParen => {
3757 self.advance();
3758 break;
3759 }
3760 other => {
3761 return Err(self.err(format!(
3762 "expected ',' or ')' in WITH (…) clause, got {other:?}"
3763 )));
3764 }
3765 }
3766 }
3767 }
3768 let partial_predicate = if matches!(self.peek(), Token::Where) {
3770 self.advance();
3771 Some(self.parse_expr(0)?)
3772 } else {
3773 None
3774 };
3775 if is_unique && !matches!(method, IndexMethod::BTree) {
3780 return Err(self.err(alloc::format!(
3781 "UNIQUE is only supported on BTree indexes, got USING {:?}",
3782 method
3783 )));
3784 }
3785 Ok(Statement::CreateIndex(CreateIndexStatement {
3786 name,
3787 table,
3788 column,
3789 method,
3790 if_not_exists,
3791 included_columns,
3792 partial_predicate,
3793 extra_columns: extra_columns.clone(),
3794 expression,
3795 is_unique,
3796 opclass,
3797 }))
3798 }
3799
3800 fn parse_column_def_with_fk(
3805 &mut self,
3806 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
3807 let col = self.parse_column_def()?;
3808 let inline_references = matches!(
3810 self.peek(),
3811 Token::Ident(s) if s.eq_ignore_ascii_case("references")
3812 );
3813 if !inline_references {
3814 return Ok((col, None));
3815 }
3816 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
3817 let fk = ForeignKeyConstraint {
3818 name: None,
3819 columns: vec![col.name.clone()],
3820 parent_table,
3821 parent_columns,
3822 on_delete,
3823 on_update,
3824 };
3825 Ok((col, Some(fk)))
3826 }
3827
3828 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
3836 let (ty, _, _) = self.parse_type_with_implied_flags()?;
3837 Ok(ty)
3838 }
3839
3840 fn parse_type_with_implied_flags(
3841 &mut self,
3842 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
3843 let ty_ident = match self.advance() {
3844 Token::Ident(s) => s,
3845 other => {
3846 return Err(ParseError {
3847 message: format!("expected column type, got {other:?}"),
3848 token_pos: self.pos.saturating_sub(1),
3849 });
3850 }
3851 };
3852 let mut implied_auto_increment = false;
3853 let mut implied_not_null = false;
3854 let mut ty = match ty_ident.as_str() {
3855 "smallserial" | "serial2" => {
3857 implied_auto_increment = true;
3858 implied_not_null = true;
3859 ColumnTypeName::SmallInt
3860 }
3861 "serial" | "serial4" => {
3862 implied_auto_increment = true;
3863 implied_not_null = true;
3864 ColumnTypeName::Int
3865 }
3866 "bigserial" | "serial8" => {
3867 implied_auto_increment = true;
3868 implied_not_null = true;
3869 ColumnTypeName::BigInt
3870 }
3871 "smallint" | "tinyint" => {
3877 self.consume_optional_paren_size();
3882 ColumnTypeName::SmallInt
3883 }
3884 "int" | "integer" | "mediumint" => {
3885 self.consume_optional_paren_size();
3886 ColumnTypeName::Int
3887 }
3888 "bigint" => {
3889 self.consume_optional_paren_size();
3890 ColumnTypeName::BigInt
3891 }
3892 "float" | "double" | "real" => {
3897 if ty_ident.eq_ignore_ascii_case("double")
3898 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
3899 {
3900 self.advance();
3901 }
3902 ColumnTypeName::Float
3903 }
3904 "float4" | "float8" => ColumnTypeName::Float,
3906 "text" => ColumnTypeName::Text,
3907 "bool" | "boolean" => ColumnTypeName::Bool,
3908 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
3909 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
3910 "vector" => {
3911 let dim = self.parse_paren_size("VECTOR")?;
3912 let encoding = self.parse_optional_vector_encoding()?;
3913 ColumnTypeName::Vector { dim, encoding }
3914 }
3915 "numeric" => {
3916 let (precision, scale) = self.parse_optional_numeric_params()?;
3917 ColumnTypeName::Numeric(precision, scale)
3918 }
3919 "date" => ColumnTypeName::Date,
3920 "timestamp" | "datetime" => {
3923 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
3929 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
3930 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
3931 {
3932 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
3936 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
3937 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
3938 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
3939 {
3940 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
3944 } else {
3945 self.consume_optional_paren_size();
3949 ColumnTypeName::Timestamp
3950 }
3951 }
3952 "timestamptz" => ColumnTypeName::Timestamptz,
3956 "json" => ColumnTypeName::Json,
3961 "jsonb" => ColumnTypeName::Jsonb,
3962 "bytea" | "bytes" => ColumnTypeName::Bytes,
3968 "tsvector" => ColumnTypeName::TsVector,
3973 "tsquery" => ColumnTypeName::TsQuery,
3974 other => {
3975 return Err(ParseError {
3976 message: format!("unsupported column type {other:?}"),
3977 token_pos: self.pos.saturating_sub(1),
3978 });
3979 }
3980 };
3981 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
3986 self.advance();
3987 }
3988 loop {
3994 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
3995 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
3996 {
3997 self.advance(); self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_))
4000 {
4001 self.advance();
4002 }
4003 continue;
4004 }
4005 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
4006 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_))
4008 {
4009 self.advance();
4010 }
4011 continue;
4012 }
4013 break;
4014 }
4015 if matches!(self.peek(), Token::LBracket) {
4020 self.advance();
4021 if !matches!(self.peek(), Token::RBracket) {
4022 return Err(self.err(alloc::format!(
4023 "TEXT[] takes no dimension; got {:?}",
4024 self.peek()
4025 )));
4026 }
4027 self.advance();
4028 ty = match ty {
4032 ColumnTypeName::Text => ColumnTypeName::TextArray,
4033 ColumnTypeName::Int => ColumnTypeName::IntArray,
4034 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
4035 other => {
4036 return Err(self.err(alloc::format!(
4037 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
4038 )));
4039 }
4040 };
4041 }
4042 Ok((ty, implied_auto_increment, implied_not_null))
4043 }
4044
4045 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
4046 let name = self.expect_ident_like()?;
4047 let (ty, implied_auto_increment, implied_not_null) =
4048 self.parse_type_with_implied_flags()?;
4049 let mut default: Option<Expr> = None;
4053 let mut nullable = !implied_not_null;
4054 let mut nullability_seen = implied_not_null;
4055 let mut auto_increment = implied_auto_increment;
4056 let mut is_primary_key = false;
4057 let mut is_unique = false;
4058 let mut check: Option<Expr> = None;
4059 loop {
4060 if matches!(self.peek(), Token::Default) {
4061 if default.is_some() {
4062 return Err(self.err("DEFAULT specified twice".into()));
4063 }
4064 self.advance();
4065 default = Some(self.parse_expr(0)?);
4066 continue;
4067 }
4068 if matches!(self.peek(), Token::Not) {
4069 if nullability_seen {
4070 return Err(self.err("NOT NULL specified twice".into()));
4071 }
4072 self.advance();
4073 if !matches!(self.peek(), Token::Null) {
4074 return Err(self.err(format!(
4075 "expected NULL after NOT in column def, got {:?}",
4076 self.peek()
4077 )));
4078 }
4079 self.advance();
4080 nullable = false;
4081 nullability_seen = true;
4082 continue;
4083 }
4084 if matches!(self.peek(), Token::Null) {
4090 if nullability_seen && !nullable {
4091 return Err(self.err(
4092 "column declared NOT NULL then NULL — pick one".into(),
4093 ));
4094 }
4095 self.advance();
4096 nullable = true;
4097 nullability_seen = true;
4098 continue;
4099 }
4100 if let Token::Ident(s) = self.peek()
4103 && (s.eq_ignore_ascii_case("auto_increment")
4104 || s.eq_ignore_ascii_case("autoincrement"))
4105 {
4106 if auto_increment {
4107 return Err(self.err("AUTO_INCREMENT specified twice".into()));
4108 }
4109 self.advance();
4110 auto_increment = true;
4111 continue;
4112 }
4113 if let Token::Ident(s) = self.peek()
4118 && s.eq_ignore_ascii_case("primary")
4119 {
4120 if is_primary_key {
4121 return Err(self.err("PRIMARY KEY specified twice".into()));
4122 }
4123 let next = self.tokens.get(self.pos + 1);
4125 let next_is_key = matches!(
4126 next,
4127 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
4128 );
4129 if !next_is_key {
4130 return Err(self.err(format!(
4131 "expected KEY after PRIMARY in column def, got {:?}",
4132 next
4133 )));
4134 }
4135 self.advance(); self.advance(); is_primary_key = true;
4138 if nullability_seen && nullable {
4139 return Err(self.err(
4140 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
4141 ));
4142 }
4143 nullable = false;
4144 nullability_seen = true;
4145 continue;
4146 }
4147 if let Token::Ident(s) = self.peek()
4151 && s.eq_ignore_ascii_case("unique")
4152 {
4153 if is_unique {
4154 return Err(self.err("UNIQUE specified twice".into()));
4155 }
4156 self.advance();
4157 is_unique = true;
4158 continue;
4159 }
4160 if let Token::Ident(s) = self.peek()
4165 && s.eq_ignore_ascii_case("check")
4166 {
4167 self.advance();
4168 if !matches!(self.peek(), Token::LParen) {
4169 return Err(self.err(alloc::format!(
4170 "expected '(' after CHECK in column def, got {:?}",
4171 self.peek()
4172 )));
4173 }
4174 self.advance();
4175 let pred = self.parse_expr(0)?;
4176 if !matches!(self.peek(), Token::RParen) {
4177 return Err(self.err(alloc::format!(
4178 "expected ')' to close CHECK predicate, got {:?}",
4179 self.peek()
4180 )));
4181 }
4182 self.advance();
4183 check = Some(match check.take() {
4184 Some(prev) => Expr::Binary {
4185 op: BinOp::And,
4186 lhs: Box::new(prev),
4187 rhs: Box::new(pred),
4188 },
4189 None => pred,
4190 });
4191 continue;
4192 }
4193 break;
4194 }
4195 Ok(ColumnDef {
4196 name,
4197 ty,
4198 nullable,
4199 default,
4200 auto_increment,
4201 is_primary_key,
4202 is_unique,
4203 check,
4204 })
4205 }
4206
4207 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
4211 if !matches!(self.peek(), Token::LParen) {
4212 return Ok((0, 0));
4216 }
4217 self.advance();
4218 let precision = match self.advance() {
4219 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
4220 other => {
4221 return Err(ParseError {
4222 message: format!(
4223 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
4224 ),
4225 token_pos: self.pos.saturating_sub(1),
4226 });
4227 }
4228 };
4229 let scale = if matches!(self.peek(), Token::Comma) {
4230 self.advance();
4231 match self.advance() {
4232 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
4233 u8::try_from(n).expect("range-checked")
4234 }
4235 other => {
4236 return Err(ParseError {
4237 message: format!(
4238 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
4239 ),
4240 token_pos: self.pos.saturating_sub(1),
4241 });
4242 }
4243 }
4244 } else {
4245 0
4246 };
4247 if !matches!(self.peek(), Token::RParen) {
4248 return Err(self.err(format!(
4249 "expected ')' to close NUMERIC params, got {:?}",
4250 self.peek()
4251 )));
4252 }
4253 self.advance();
4254 Ok((precision, scale))
4255 }
4256
4257 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
4265 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4266 return Ok(VecEncoding::F32);
4267 }
4268 let n1 = self.tokens.get(self.pos + 1);
4274 let next_is_encoding = matches!(
4275 n1,
4276 Some(Token::Ident(s))
4277 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
4278 );
4279 if !next_is_encoding {
4280 return Ok(VecEncoding::F32);
4281 }
4282 self.advance();
4283 let enc_ident = match self.advance() {
4284 Token::Ident(s) => s,
4285 other => {
4286 return Err(self.err(format!(
4287 "expected vector encoding after USING, got {other:?}"
4288 )));
4289 }
4290 };
4291 match enc_ident.to_ascii_lowercase().as_str() {
4292 "sq8" => Ok(VecEncoding::Sq8),
4293 "half" => Ok(VecEncoding::F16),
4296 other => Err(self.err(format!(
4297 "unknown vector encoding {other:?}; supported: SQ8, HALF"
4298 ))),
4299 }
4300 }
4301
4302 fn consume_optional_paren_size(&mut self) {
4306 if !matches!(self.peek(), Token::LParen) {
4307 return;
4308 }
4309 self.advance();
4310 let mut depth = 1usize;
4312 while depth > 0 {
4313 match self.peek() {
4314 Token::LParen => depth += 1,
4315 Token::RParen => depth -= 1,
4316 Token::Eof => return,
4317 _ => {}
4318 }
4319 self.advance();
4320 }
4321 }
4322
4323 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
4324 if !matches!(self.peek(), Token::LParen) {
4325 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
4326 }
4327 self.advance();
4328 let n = match self.advance() {
4329 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
4330 message: format!("{label} size too large: {n}"),
4331 token_pos: self.pos.saturating_sub(1),
4332 })?,
4333 other => {
4334 return Err(ParseError {
4335 message: format!("expected positive integer {label} size, got {other:?}"),
4336 token_pos: self.pos.saturating_sub(1),
4337 });
4338 }
4339 };
4340 if !matches!(self.peek(), Token::RParen) {
4341 return Err(self.err(format!(
4342 "expected ')' after {label} size, got {:?}",
4343 self.peek()
4344 )));
4345 }
4346 self.advance();
4347 Ok(n)
4348 }
4349
4350 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
4351 debug_assert!(matches!(self.peek(), Token::Insert));
4352 self.advance();
4353 if !matches!(self.peek(), Token::Into) {
4354 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
4355 }
4356 self.advance();
4357 let table = self.expect_ident_like()?;
4358 let columns = if matches!(self.peek(), Token::LParen) {
4360 self.advance();
4361 let mut names = Vec::new();
4362 loop {
4363 names.push(self.expect_ident_like()?);
4364 match self.peek() {
4365 Token::Comma => {
4366 self.advance();
4367 }
4368 Token::RParen => {
4369 self.advance();
4370 break;
4371 }
4372 other => {
4373 return Err(self.err(format!(
4374 "expected ',' or ')' in INSERT column list, got {other:?}"
4375 )));
4376 }
4377 }
4378 }
4379 Some(names)
4380 } else {
4381 None
4382 };
4383 if matches!(self.peek(), Token::Select) {
4386 let select_stmt = match self.parse_select_stmt()? {
4387 Statement::Select(s) => s,
4388 other => {
4389 return Err(self.err(alloc::format!(
4390 "expected SELECT after INSERT INTO ... target, got {other:?}"
4391 )));
4392 }
4393 };
4394 let on_conflict = self.parse_optional_on_conflict()?;
4395 let returning = self.parse_optional_returning()?;
4396 return Ok(Statement::Insert(InsertStatement {
4397 table,
4398 columns,
4399 rows: Vec::new(),
4400 select_source: Some(Box::new(select_stmt)),
4401 on_conflict,
4402 returning,
4403 }));
4404 }
4405 if !matches!(self.peek(), Token::Values) {
4406 return Err(self.err(format!(
4407 "expected VALUES or SELECT after table name, got {:?}",
4408 self.peek()
4409 )));
4410 }
4411 self.advance();
4412 if !matches!(self.peek(), Token::LParen) {
4413 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
4414 }
4415 let mut rows = Vec::new();
4416 loop {
4417 if !matches!(self.peek(), Token::LParen) {
4419 return Err(self.err(format!(
4420 "expected '(' for next VALUES tuple, got {:?}",
4421 self.peek()
4422 )));
4423 }
4424 self.advance();
4425 let mut tuple = Vec::new();
4426 loop {
4427 tuple.push(self.parse_expr(0)?);
4428 match self.peek() {
4429 Token::Comma => {
4430 self.advance();
4431 }
4432 Token::RParen => {
4433 self.advance();
4434 break;
4435 }
4436 other => {
4437 return Err(self.err(format!(
4438 "expected ',' or ')' in VALUES tuple, got {other:?}"
4439 )));
4440 }
4441 }
4442 }
4443 if tuple.is_empty() {
4444 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
4445 }
4446 rows.push(tuple);
4447 if matches!(self.peek(), Token::Comma) {
4449 self.advance();
4450 } else {
4451 break;
4452 }
4453 }
4454 let on_conflict = self.parse_optional_on_conflict()?;
4455 let returning = self.parse_optional_returning()?;
4456 Ok(Statement::Insert(InsertStatement {
4457 table,
4458 columns,
4459 rows,
4460 select_source: None,
4461 on_conflict,
4462 returning,
4463 }))
4464 }
4465
4466 fn parse_optional_on_conflict(
4471 &mut self,
4472 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
4473 if !matches!(self.peek(), Token::On) {
4474 return Ok(None);
4475 }
4476 let next_is_conflict = matches!(
4479 self.tokens.get(self.pos + 1),
4480 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
4481 );
4482 if !next_is_conflict {
4483 return Ok(None);
4484 }
4485 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
4489 if matches!(self.peek(), Token::LParen) {
4490 self.advance();
4491 loop {
4492 target_columns.push(self.expect_ident_like()?);
4493 match self.peek() {
4494 Token::Comma => {
4495 self.advance();
4496 }
4497 Token::RParen => {
4498 self.advance();
4499 break;
4500 }
4501 other => {
4502 return Err(self.err(alloc::format!(
4503 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
4504 )));
4505 }
4506 }
4507 }
4508 }
4509 match self.advance() {
4511 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
4512 other => {
4513 return Err(self.err(alloc::format!(
4514 "expected DO after ON CONFLICT [(…)], got {other:?}"
4515 )));
4516 }
4517 }
4518 let action = match self.advance() {
4520 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
4521 crate::ast::OnConflictAction::Nothing
4522 }
4523 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
4524 self.parse_on_conflict_update_action()?
4525 }
4526 other => {
4527 return Err(self.err(alloc::format!(
4528 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
4529 )));
4530 }
4531 };
4532 Ok(Some(crate::ast::OnConflictClause {
4533 target_columns,
4534 action,
4535 }))
4536 }
4537
4538 fn parse_on_conflict_update_action(
4542 &mut self,
4543 ) -> Result<crate::ast::OnConflictAction, ParseError> {
4544 match self.advance() {
4546 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
4547 other => {
4548 return Err(self.err(alloc::format!(
4549 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
4550 )));
4551 }
4552 }
4553 let mut assignments: Vec<(String, Expr)> = Vec::new();
4554 loop {
4555 let col = self.expect_ident_like()?;
4556 if !matches!(self.peek(), Token::Eq) {
4557 return Err(self.err(alloc::format!(
4558 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
4559 self.peek()
4560 )));
4561 }
4562 self.advance();
4563 let value = self.parse_expr(0)?;
4564 assignments.push((col, value));
4565 if matches!(self.peek(), Token::Comma) {
4566 self.advance();
4567 continue;
4568 }
4569 break;
4570 }
4571 let where_ = if matches!(self.peek(), Token::Where) {
4572 self.advance();
4573 Some(self.parse_expr(0)?)
4574 } else {
4575 None
4576 };
4577 Ok(crate::ast::OnConflictAction::Update {
4578 assignments,
4579 where_,
4580 })
4581 }
4582
4583 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
4584 let mut items = Vec::new();
4585 loop {
4586 items.push(self.parse_select_item()?);
4587 if matches!(self.peek(), Token::Comma) {
4588 self.advance();
4589 } else {
4590 break;
4591 }
4592 }
4593 Ok(items)
4594 }
4595
4596 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
4597 if matches!(self.peek(), Token::Star) {
4598 self.advance();
4599 return Ok(SelectItem::Wildcard);
4600 }
4601 let expr = self.parse_expr(0)?;
4602 let alias = self.parse_optional_alias();
4603 Ok(SelectItem::Expr { expr, alias })
4604 }
4605
4606 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
4607 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
4611 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4612 {
4613 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
4616 if !matches!(self.peek(), Token::RParen) {
4617 return Err(self.err(alloc::format!(
4618 "expected ')' after unnest() argument, got {:?}",
4619 self.peek()
4620 )));
4621 }
4622 self.advance();
4623 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
4624 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
4625 return Ok(TableRef {
4626 name,
4627 alias: alias_ident,
4628 as_of_segment: None,
4629 unnest_expr: Some(Box::new(expr)),
4630 unnest_column_aliases,
4631 });
4632 }
4633 let name = self.expect_ident_like()?;
4634 let as_of_segment = if matches!(self.peek(), Token::As)
4640 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
4641 {
4642 self.advance(); self.advance(); let kw = match self.peek().clone() {
4645 Token::Ident(s) | Token::QuotedIdent(s) => s,
4646 other => {
4647 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
4648 }
4649 };
4650 if !kw.eq_ignore_ascii_case("segment") {
4651 return Err(self.err(format!(
4652 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
4653 )));
4654 }
4655 self.advance();
4656 let id = match self.advance() {
4659 Token::String(s) => s
4660 .parse::<u32>()
4661 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4662 Token::Integer(n) => u32::try_from(n)
4663 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4664 other => {
4665 return Err(self.err(format!(
4666 "expected segment id literal after AS OF SEGMENT, got {other:?}"
4667 )));
4668 }
4669 };
4670 Some(id)
4671 } else {
4672 None
4673 };
4674 let alias = self.parse_optional_alias();
4675 Ok(TableRef {
4676 name,
4677 alias,
4678 as_of_segment,
4679 unnest_expr: None,
4680 unnest_column_aliases: Vec::new(),
4681 })
4682 }
4683
4684 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
4690 let alias = self.parse_optional_alias();
4691 if alias.is_none() {
4692 return (None, Vec::new());
4693 }
4694 let mut cols: Vec<String> = Vec::new();
4695 if matches!(self.peek(), Token::LParen) {
4696 self.advance();
4697 loop {
4698 match self.peek().clone() {
4699 Token::Ident(s) | Token::QuotedIdent(s) => {
4700 self.advance();
4701 cols.push(s);
4702 }
4703 _ => break,
4704 }
4705 if matches!(self.peek(), Token::Comma) {
4706 self.advance();
4707 continue;
4708 }
4709 break;
4710 }
4711 if matches!(self.peek(), Token::RParen) {
4712 self.advance();
4713 }
4714 }
4715 (alias, cols)
4716 }
4717
4718 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
4723 let primary = self.parse_table_ref()?;
4724 let mut joins = Vec::new();
4725 loop {
4726 if matches!(self.peek(), Token::Comma) {
4728 self.advance();
4729 let table = self.parse_table_ref()?;
4730 joins.push(FromJoin {
4731 kind: JoinKind::Cross,
4732 table,
4733 on: None,
4734 });
4735 continue;
4736 }
4737 let kind =
4740 match self.peek() {
4741 Token::Inner => {
4742 self.advance();
4743 if !matches!(self.peek(), Token::Join) {
4744 return Err(self
4745 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
4746 }
4747 self.advance();
4748 JoinKind::Inner
4749 }
4750 Token::Left => {
4751 self.advance();
4752 if matches!(self.peek(), Token::Outer) {
4753 self.advance();
4754 }
4755 if !matches!(self.peek(), Token::Join) {
4756 return Err(self.err(format!(
4757 "expected JOIN after LEFT [OUTER], got {:?}",
4758 self.peek()
4759 )));
4760 }
4761 self.advance();
4762 JoinKind::Left
4763 }
4764 Token::Cross => {
4765 self.advance();
4766 if !matches!(self.peek(), Token::Join) {
4767 return Err(self
4768 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
4769 }
4770 self.advance();
4771 JoinKind::Cross
4772 }
4773 Token::Join => {
4774 self.advance();
4775 JoinKind::Inner
4776 }
4777 _ => break,
4778 };
4779 let table = self.parse_table_ref()?;
4780 let on = if matches!(self.peek(), Token::On) {
4781 self.advance();
4782 Some(self.parse_expr(0)?)
4783 } else if kind == JoinKind::Cross {
4784 None
4785 } else {
4786 return Err(self.err(format!(
4787 "expected ON after {:?} JOIN, got {:?}",
4788 kind,
4789 self.peek()
4790 )));
4791 };
4792 joins.push(FromJoin { kind, table, on });
4793 }
4794 Ok(FromClause { primary, joins })
4795 }
4796
4797 fn parse_optional_alias(&mut self) -> Option<String> {
4802 if matches!(self.peek(), Token::As) {
4803 self.advance();
4804 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
4809 return self.expect_ident_like().ok();
4810 }
4811 return None;
4812 }
4813 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
4814 return self.expect_ident_like().ok();
4815 }
4816 None
4817 }
4818
4819 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
4821 let mut lhs = self.parse_unary()?;
4822 while let Some((op, prec)) = binop_from(self.peek()) {
4823 if prec < min_prec {
4824 break;
4825 }
4826 self.advance();
4827 let any_kind = match self.peek() {
4832 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
4833 Some(false)
4834 }
4835 Token::Ident(s) | Token::QuotedIdent(s)
4836 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
4837 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
4838 {
4839 Some(s.eq_ignore_ascii_case("any"))
4840 }
4841 _ => None,
4842 };
4843 if let Some(is_any) = any_kind {
4844 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
4847 if !matches!(self.peek(), Token::RParen) {
4848 return Err(self.err(alloc::format!(
4849 "expected ')' after ANY/ALL argument, got {:?}",
4850 self.peek()
4851 )));
4852 }
4853 self.advance();
4854 lhs = Expr::AnyAll {
4855 expr: Box::new(lhs),
4856 op,
4857 array: Box::new(arr),
4858 is_any,
4859 };
4860 continue;
4861 }
4862 let rhs = self.parse_expr(prec + 1)?;
4863 lhs = Expr::Binary {
4864 lhs: Box::new(lhs),
4865 op,
4866 rhs: Box::new(rhs),
4867 };
4868 }
4869 Ok(lhs)
4870 }
4871
4872 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
4873 match self.peek() {
4874 Token::Not => {
4875 self.advance();
4876 let e = self.parse_expr(3)?;
4879 Ok(Expr::Unary {
4880 op: UnOp::Not,
4881 expr: Box::new(e),
4882 })
4883 }
4884 Token::Minus => {
4885 self.advance();
4886 let e = self.parse_expr(8)?;
4889 Ok(Expr::Unary {
4890 op: UnOp::Neg,
4891 expr: Box::new(e),
4892 })
4893 }
4894 _ => self.parse_atom(),
4895 }
4896 }
4897
4898 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
4899 let tok_pos = self.pos;
4900 match self.advance() {
4901 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
4902 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
4903 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
4904 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
4905 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
4906 Token::Null => Ok(Expr::Literal(Literal::Null)),
4907 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
4911 Token::LParen => {
4912 if matches!(self.peek(), Token::Select) {
4916 let inner = self.parse_select_stmt()?;
4917 match self.advance() {
4918 Token::RParen => {
4919 let Statement::Select(s) = inner else {
4920 unreachable!("parse_select_stmt returns Select")
4921 };
4922 Ok(Expr::ScalarSubquery(Box::new(s)))
4923 }
4924 other => Err(ParseError {
4925 message: format!("expected ')' after scalar subquery, got {other:?}"),
4926 token_pos: self.pos.saturating_sub(1),
4927 }),
4928 }
4929 } else {
4930 let e = self.parse_expr(0)?;
4931 match self.advance() {
4932 Token::RParen => Ok(e),
4933 other => Err(ParseError {
4934 message: format!("expected ')', got {other:?}"),
4935 token_pos: self.pos.saturating_sub(1),
4936 }),
4937 }
4938 }
4939 }
4940 Token::LBracket => self.parse_vector_literal_body(),
4941 Token::Extract => self.parse_extract_atom(),
4942 Token::Interval => self.parse_interval_atom(),
4943 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
4948 self.parse_exists_atom(false)
4949 }
4950 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
4954 self.parse_case_atom()
4955 }
4956 Token::Ident(s) | Token::QuotedIdent(s)
4960 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
4961 {
4962 self.advance(); let mut items: Vec<Expr> = Vec::new();
4964 if !matches!(self.peek(), Token::RBracket) {
4965 loop {
4966 items.push(self.parse_expr(0)?);
4967 match self.peek() {
4968 Token::Comma => {
4969 self.advance();
4970 }
4971 Token::RBracket => break,
4972 other => {
4973 return Err(self.err(alloc::format!(
4974 "expected ',' or ']' in ARRAY literal, got {other:?}"
4975 )));
4976 }
4977 }
4978 }
4979 }
4980 self.advance(); Ok(Expr::Array(items))
4982 }
4983 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
4984 other => Err(ParseError {
4985 message: format!("unexpected token {other:?} in expression"),
4986 token_pos: tok_pos,
4987 }),
4988 }
4989 .and_then(|atom| self.finish_postfix_casts(atom))
4991 }
4992
4993 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
4996 loop {
4997 if matches!(self.peek(), Token::DoubleColon) {
4998 self.advance();
4999 let target = match self.advance() {
5004 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
5005 "int" | "integer" | "int4" => {
5006 if matches!(self.peek(), Token::LBracket)
5007 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5008 {
5009 self.advance();
5010 self.advance();
5011 CastTarget::IntArray
5012 } else {
5013 CastTarget::Int
5014 }
5015 }
5016 "bigint" | "int8" => {
5017 if matches!(self.peek(), Token::LBracket)
5018 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5019 {
5020 self.advance();
5021 self.advance();
5022 CastTarget::BigIntArray
5023 } else {
5024 CastTarget::BigInt
5025 }
5026 }
5027 "float" | "double" | "real" => CastTarget::Float,
5028 "text" => {
5029 if matches!(self.peek(), Token::LBracket)
5031 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5032 {
5033 self.advance();
5034 self.advance();
5035 CastTarget::TextArray
5036 } else {
5037 CastTarget::Text
5038 }
5039 }
5040 "bool" | "boolean" => CastTarget::Bool,
5041 "vector" => CastTarget::Vector,
5042 "date" => CastTarget::Date,
5043 "timestamp" | "datetime" => CastTarget::Timestamp,
5044 "timestamptz" => CastTarget::Timestamptz,
5045 "interval" => CastTarget::Interval,
5046 "json" => CastTarget::Json,
5047 "jsonb" => CastTarget::Jsonb,
5048 "regtype" => CastTarget::RegType,
5049 "regclass" => CastTarget::RegClass,
5050 "tsvector" => CastTarget::TsVector,
5054 "tsquery" => CastTarget::TsQuery,
5055 other => {
5056 return Err(ParseError {
5057 message: format!("unsupported cast target `::{other}`"),
5058 token_pos: self.pos.saturating_sub(1),
5059 });
5060 }
5061 },
5062 Token::Interval => CastTarget::Interval,
5063 other => {
5064 return Err(ParseError {
5065 message: format!("expected type ident after `::`, got {other:?}"),
5066 token_pos: self.pos.saturating_sub(1),
5067 });
5068 }
5069 };
5070 expr = Expr::Cast {
5071 expr: Box::new(expr),
5072 target,
5073 };
5074 continue;
5075 }
5076 if matches!(self.peek(), Token::Is) {
5077 self.advance();
5078 let negated = if matches!(self.peek(), Token::Not) {
5079 self.advance();
5080 true
5081 } else {
5082 false
5083 };
5084 if matches!(self.peek(), Token::Distinct) {
5087 self.advance();
5088 if !matches!(self.peek(), Token::From) {
5089 return Err(self.err(format!(
5090 "expected FROM after IS{} DISTINCT, got {:?}",
5091 if negated { " NOT" } else { "" },
5092 self.peek()
5093 )));
5094 }
5095 self.advance();
5096 let rhs = self.parse_expr(20)?;
5100 let op = if negated {
5101 BinOp::IsNotDistinctFrom
5102 } else {
5103 BinOp::IsDistinctFrom
5104 };
5105 expr = Expr::Binary {
5106 op,
5107 lhs: Box::new(expr),
5108 rhs: Box::new(rhs),
5109 };
5110 continue;
5111 }
5112 if !matches!(self.peek(), Token::Null) {
5113 return Err(self.err(format!(
5114 "expected NULL or DISTINCT after IS{}, got {:?}",
5115 if negated { " NOT" } else { "" },
5116 self.peek()
5117 )));
5118 }
5119 self.advance();
5120 expr = Expr::IsNull {
5121 expr: Box::new(expr),
5122 negated,
5123 };
5124 continue;
5125 }
5126 let negated = if matches!(self.peek(), Token::Not) {
5130 let next = self.tokens.get(self.pos + 1);
5131 matches!(next, Some(Token::Between | Token::In | Token::Like))
5132 } else {
5133 false
5134 };
5135 if negated {
5136 self.advance();
5137 }
5138 if matches!(self.peek(), Token::Between) {
5139 expr = self.parse_between_tail(expr, negated)?;
5140 continue;
5141 }
5142 if matches!(self.peek(), Token::In) {
5143 expr = self.parse_in_tail(expr, negated)?;
5144 continue;
5145 }
5146 if matches!(self.peek(), Token::Like) {
5147 self.advance();
5148 let pattern = self.parse_expr(5)?;
5151 expr = Expr::Like {
5152 expr: Box::new(expr),
5153 pattern: Box::new(pattern),
5154 negated,
5155 };
5156 continue;
5157 }
5158 if matches!(self.peek(), Token::LBracket) {
5162 self.advance();
5163 let index = self.parse_expr(0)?;
5164 if !matches!(self.peek(), Token::RBracket) {
5165 return Err(self.err(alloc::format!(
5166 "expected ']' after array index, got {:?}",
5167 self.peek()
5168 )));
5169 }
5170 self.advance();
5171 expr = Expr::ArraySubscript {
5172 target: Box::new(expr),
5173 index: Box::new(index),
5174 };
5175 continue;
5176 }
5177 return Ok(expr);
5178 }
5179 }
5180
5181 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5185 self.advance(); let low = self.parse_expr(5)?;
5187 if !matches!(self.peek(), Token::And) {
5188 return Err(self.err(format!(
5189 "expected AND after BETWEEN low bound, got {:?}",
5190 self.peek()
5191 )));
5192 }
5193 self.advance();
5194 let high = self.parse_expr(5)?;
5195 let target = Box::new(expr);
5196 let combined = Expr::Binary {
5197 lhs: Box::new(Expr::Binary {
5198 lhs: target.clone(),
5199 op: BinOp::GtEq,
5200 rhs: Box::new(low),
5201 }),
5202 op: BinOp::And,
5203 rhs: Box::new(Expr::Binary {
5204 lhs: target,
5205 op: BinOp::LtEq,
5206 rhs: Box::new(high),
5207 }),
5208 };
5209 Ok(maybe_not(combined, negated))
5210 }
5211
5212 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
5217 let mut recursive = false;
5222 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5223 && s.eq_ignore_ascii_case("recursive")
5224 {
5225 self.advance();
5226 recursive = true;
5227 }
5228 let mut ctes = Vec::new();
5229 loop {
5230 let name = self.expect_ident_like()?;
5231 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
5235 self.advance();
5236 let mut names = Vec::new();
5237 loop {
5238 names.push(self.expect_ident_like()?);
5239 if matches!(self.peek(), Token::Comma) {
5240 self.advance();
5241 continue;
5242 }
5243 break;
5244 }
5245 if !matches!(self.peek(), Token::RParen) {
5246 return Err(self.err(format!(
5247 "expected ')' to close CTE column list, got {:?}",
5248 self.peek()
5249 )));
5250 }
5251 self.advance();
5252 names
5253 } else {
5254 Vec::new()
5255 };
5256 if !matches!(self.peek(), Token::As) {
5260 return Err(self.err(format!(
5261 "expected AS after CTE name {name:?}, got {:?}",
5262 self.peek()
5263 )));
5264 }
5265 self.advance();
5266 if !matches!(self.peek(), Token::LParen) {
5267 return Err(self.err(format!(
5268 "expected '(' after AS in WITH clause, got {:?}",
5269 self.peek()
5270 )));
5271 }
5272 self.advance();
5273 if !matches!(self.peek(), Token::Select) {
5274 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
5275 }
5276 let inner = self.parse_select_stmt()?;
5277 if !matches!(self.peek(), Token::RParen) {
5278 return Err(self.err(format!(
5279 "expected ')' after CTE body, got {:?}",
5280 self.peek()
5281 )));
5282 }
5283 self.advance();
5284 let Statement::Select(body) = inner else {
5285 unreachable!("parse_select_stmt returns Select")
5286 };
5287 ctes.push(crate::ast::Cte {
5288 name,
5289 body,
5290 recursive,
5291 column_overrides,
5292 });
5293 if matches!(self.peek(), Token::Comma) {
5294 self.advance();
5295 continue;
5296 }
5297 break;
5298 }
5299 if !matches!(self.peek(), Token::Select) {
5301 return Err(self.err(format!(
5302 "expected SELECT after WITH clause, got {:?}",
5303 self.peek()
5304 )));
5305 }
5306 let body_stmt = self.parse_select_stmt()?;
5307 let Statement::Select(mut body) = body_stmt else {
5308 unreachable!()
5309 };
5310 body.ctes = ctes;
5311 Ok(Statement::Select(body))
5312 }
5313
5314 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
5323 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
5327 None
5328 } else {
5329 Some(Box::new(self.parse_expr(0)?))
5330 };
5331 let mut branches: Vec<(Expr, Expr)> = Vec::new();
5332 loop {
5333 match self.peek() {
5334 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
5335 self.advance();
5336 let cond = self.parse_expr(0)?;
5337 match self.peek() {
5338 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
5339 self.advance();
5340 }
5341 other => {
5342 return Err(self.err(alloc::format!(
5343 "expected THEN after CASE WHEN <expr>, got {other:?}"
5344 )));
5345 }
5346 }
5347 let value = self.parse_expr(0)?;
5348 branches.push((cond, value));
5349 }
5350 _ => break,
5351 }
5352 }
5353 if branches.is_empty() {
5354 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
5355 }
5356 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
5357 {
5358 self.advance();
5359 Some(Box::new(self.parse_expr(0)?))
5360 } else {
5361 None
5362 };
5363 match self.peek() {
5364 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
5365 self.advance();
5366 }
5367 other => {
5368 return Err(self.err(alloc::format!(
5369 "expected END to close CASE expression, got {other:?}"
5370 )));
5371 }
5372 }
5373 Ok(Expr::Case {
5374 operand,
5375 branches,
5376 else_branch,
5377 })
5378 }
5379
5380 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
5381 if !matches!(self.peek(), Token::LParen) {
5382 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
5383 }
5384 self.advance();
5385 let inner = self.parse_select_stmt()?;
5386 if !matches!(self.peek(), Token::RParen) {
5387 return Err(self.err(format!(
5388 "expected ')' after EXISTS-subquery, got {:?}",
5389 self.peek()
5390 )));
5391 }
5392 self.advance();
5393 let Statement::Select(s) = inner else {
5394 unreachable!("parse_select_stmt returns Select")
5395 };
5396 Ok(Expr::Exists {
5397 subquery: Box::new(s),
5398 negated,
5399 })
5400 }
5401
5402 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5403 self.advance(); if !matches!(self.peek(), Token::LParen) {
5405 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
5406 }
5407 self.advance();
5408 if matches!(self.peek(), Token::Select) {
5410 let inner = self.parse_select_stmt()?;
5411 if !matches!(self.peek(), Token::RParen) {
5412 return Err(self.err(format!(
5413 "expected ')' after IN-subquery, got {:?}",
5414 self.peek()
5415 )));
5416 }
5417 self.advance();
5418 let Statement::Select(s) = inner else {
5419 unreachable!("parse_select_stmt always returns Statement::Select")
5420 };
5421 return Ok(Expr::InSubquery {
5422 expr: Box::new(expr),
5423 subquery: Box::new(s),
5424 negated,
5425 });
5426 }
5427 let mut elements = Vec::new();
5428 if !matches!(self.peek(), Token::RParen) {
5429 loop {
5430 elements.push(self.parse_expr(0)?);
5431 match self.peek() {
5432 Token::Comma => {
5433 self.advance();
5434 }
5435 Token::RParen => break,
5436 other => {
5437 return Err(
5438 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
5439 );
5440 }
5441 }
5442 }
5443 }
5444 self.advance(); let target = Box::new(expr);
5446 let combined = if elements.is_empty() {
5447 Expr::Literal(Literal::Bool(false))
5448 } else {
5449 let mut iter = elements.into_iter();
5450 let first = iter.next().unwrap();
5451 let mut acc = Expr::Binary {
5452 lhs: target.clone(),
5453 op: BinOp::Eq,
5454 rhs: Box::new(first),
5455 };
5456 for elt in iter {
5457 acc = Expr::Binary {
5458 lhs: Box::new(acc),
5459 op: BinOp::Or,
5460 rhs: Box::new(Expr::Binary {
5461 lhs: target.clone(),
5462 op: BinOp::Eq,
5463 rhs: Box::new(elt),
5464 }),
5465 };
5466 }
5467 acc
5468 };
5469 Ok(maybe_not(combined, negated))
5470 }
5471
5472 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
5480 if !matches!(self.peek(), Token::LParen) {
5481 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
5482 }
5483 self.advance();
5484 let field_name = self.expect_ident_like()?;
5485 let field = match field_name.to_ascii_lowercase().as_str() {
5486 "year" => ExtractField::Year,
5487 "month" => ExtractField::Month,
5488 "day" => ExtractField::Day,
5489 "hour" => ExtractField::Hour,
5490 "minute" => ExtractField::Minute,
5491 "second" => ExtractField::Second,
5492 "microsecond" | "microseconds" => ExtractField::Microsecond,
5493 other => {
5494 return Err(self.err(format!(
5495 "unknown EXTRACT field {other:?}; \
5496 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
5497 )));
5498 }
5499 };
5500 if !matches!(self.peek(), Token::From) {
5501 return Err(self.err(format!(
5502 "expected FROM after EXTRACT field, got {:?}",
5503 self.peek()
5504 )));
5505 }
5506 self.advance();
5507 let source = self.parse_expr(0)?;
5508 if !matches!(self.peek(), Token::RParen) {
5509 return Err(self.err(format!(
5510 "expected ')' to close EXTRACT, got {:?}",
5511 self.peek()
5512 )));
5513 }
5514 self.advance();
5515 Ok(Expr::Extract {
5516 field,
5517 source: Box::new(source),
5518 })
5519 }
5520
5521 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
5526 let tok = self.advance();
5527 let Token::String(text) = tok else {
5528 return Err(self.err(format!(
5529 "expected string literal after INTERVAL, got {tok:?}"
5530 )));
5531 };
5532 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
5533 message: format!(
5534 "cannot parse INTERVAL {text:?}; \
5535 expected `<n> <unit> [<n> <unit> ...]` with units \
5536 microsecond[s], millisecond[s], second[s], minute[s], \
5537 hour[s], day[s], week[s], month[s], year[s]"
5538 ),
5539 token_pos: self.pos.saturating_sub(1),
5540 })?;
5541 Ok(Expr::Literal(Literal::Interval {
5542 months,
5543 micros,
5544 text,
5545 }))
5546 }
5547
5548 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
5549 let mut elems = Vec::new();
5550 if matches!(self.peek(), Token::RBracket) {
5551 self.advance();
5552 return Ok(Expr::Literal(Literal::Vector(elems)));
5553 }
5554 loop {
5555 let e = self.parse_expr(0)?;
5556 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
5557 message: format!("vector element must be a numeric literal, got {e:?}"),
5558 token_pos: self.pos,
5559 })?;
5560 elems.push(x);
5561 match self.peek() {
5562 Token::Comma => {
5563 self.advance();
5564 }
5565 Token::RBracket => {
5566 self.advance();
5567 break;
5568 }
5569 other => {
5570 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
5571 }
5572 }
5573 }
5574 Ok(Expr::Literal(Literal::Vector(elems)))
5575 }
5576
5577 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
5586 let Token::Ident(s) = self.peek().clone() else {
5587 return NullTreatment::Respect;
5588 };
5589 let is_ignore = s.eq_ignore_ascii_case("ignore");
5590 let is_respect = s.eq_ignore_ascii_case("respect");
5591 if !is_ignore && !is_respect {
5592 return NullTreatment::Respect;
5593 }
5594 if self.pos + 1 < self.tokens.len()
5597 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
5598 && s2.eq_ignore_ascii_case("nulls")
5599 {
5600 self.advance();
5601 self.advance();
5602 return if is_ignore {
5603 NullTreatment::Ignore
5604 } else {
5605 NullTreatment::Respect
5606 };
5607 }
5608 NullTreatment::Respect
5609 }
5610
5611 #[allow(clippy::type_complexity)] fn parse_over_clause(
5614 &mut self,
5615 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
5616 if !matches!(self.peek(), Token::LParen) {
5617 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
5618 }
5619 self.advance();
5620 let mut partition_by = Vec::new();
5621 let mut order_by = Vec::new();
5622 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5624 && s.eq_ignore_ascii_case("partition")
5625 {
5626 self.advance();
5627 if !matches!(self.peek(), Token::By) {
5628 return Err(self.err(format!(
5629 "expected BY after PARTITION, got {:?}",
5630 self.peek()
5631 )));
5632 }
5633 self.advance();
5634 loop {
5635 partition_by.push(self.parse_expr(0)?);
5636 if matches!(self.peek(), Token::Comma) {
5637 self.advance();
5638 continue;
5639 }
5640 break;
5641 }
5642 }
5643 if matches!(self.peek(), Token::Order) {
5645 self.advance();
5646 if !matches!(self.peek(), Token::By) {
5647 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
5648 }
5649 self.advance();
5650 loop {
5651 let e = self.parse_expr(0)?;
5652 let desc = if matches!(self.peek(), Token::Desc) {
5653 self.advance();
5654 true
5655 } else if matches!(self.peek(), Token::Asc) {
5656 self.advance();
5657 false
5658 } else {
5659 false
5660 };
5661 order_by.push((e, desc));
5662 if matches!(self.peek(), Token::Comma) {
5663 self.advance();
5664 continue;
5665 }
5666 break;
5667 }
5668 }
5669 let mut frame: Option<WindowFrame> = None;
5673 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
5674 let kind = if s.eq_ignore_ascii_case("rows") {
5675 Some(FrameKind::Rows)
5676 } else if s.eq_ignore_ascii_case("range") {
5677 Some(FrameKind::Range)
5678 } else {
5679 None
5680 };
5681 if let Some(kind) = kind {
5682 self.advance();
5683 frame = Some(self.parse_frame_tail(kind)?);
5684 }
5685 }
5686 if !matches!(self.peek(), Token::RParen) {
5687 return Err(self.err(format!(
5688 "expected ')' to close OVER clause, got {:?}",
5689 self.peek()
5690 )));
5691 }
5692 self.advance();
5693 Ok((partition_by, order_by, frame))
5694 }
5695
5696 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
5702 if matches!(self.peek(), Token::Between) {
5703 self.advance();
5704 let start = self.parse_frame_bound()?;
5705 if !matches!(self.peek(), Token::And) {
5706 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
5707 }
5708 self.advance();
5709 let end = self.parse_frame_bound()?;
5710 Ok(WindowFrame {
5711 kind,
5712 start,
5713 end: Some(end),
5714 })
5715 } else {
5716 let start = self.parse_frame_bound()?;
5717 Ok(WindowFrame {
5718 kind,
5719 start,
5720 end: None,
5721 })
5722 }
5723 }
5724
5725 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
5728 if let Token::Integer(n) = *self.peek() {
5730 self.advance();
5731 let n: u64 = u64::try_from(n).map_err(|_| {
5732 self.err(format!(
5733 "invalid frame offset {n} — expected non-negative integer"
5734 ))
5735 })?;
5736 let dir = self.expect_ident_like()?;
5737 return if dir.eq_ignore_ascii_case("preceding") {
5738 Ok(FrameBound::OffsetPreceding(n))
5739 } else if dir.eq_ignore_ascii_case("following") {
5740 Ok(FrameBound::OffsetFollowing(n))
5741 } else {
5742 Err(self.err(format!(
5743 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
5744 )))
5745 };
5746 }
5747 let first = self.expect_ident_like()?;
5748 if first.eq_ignore_ascii_case("unbounded") {
5749 let dir = self.expect_ident_like()?;
5750 return if dir.eq_ignore_ascii_case("preceding") {
5751 Ok(FrameBound::UnboundedPreceding)
5752 } else if dir.eq_ignore_ascii_case("following") {
5753 Ok(FrameBound::UnboundedFollowing)
5754 } else {
5755 Err(self.err(format!(
5756 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
5757 )))
5758 };
5759 }
5760 if first.eq_ignore_ascii_case("current") {
5761 let row = self.expect_ident_like()?;
5762 if !row.eq_ignore_ascii_case("row") {
5763 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
5764 }
5765 return Ok(FrameBound::CurrentRow);
5766 }
5767 Err(self.err(format!(
5768 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
5769 )))
5770 }
5771
5772 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
5773 if matches!(self.peek(), Token::Dot) {
5774 self.advance();
5775 let name = self.expect_ident_like()?;
5776 if matches!(self.peek(), Token::LParen) {
5782 return self.finish_ident_atom(name);
5783 }
5784 return Ok(Expr::Column(ColumnName {
5785 qualifier: Some(first),
5786 name,
5787 }));
5788 }
5789 if matches!(self.peek(), Token::LParen) {
5790 self.advance();
5791 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
5795 self.advance();
5796 if !matches!(self.peek(), Token::RParen) {
5797 return Err(self.err(format!(
5798 "expected ')' after COUNT(*), got {:?}",
5799 self.peek()
5800 )));
5801 }
5802 self.advance();
5803 let null_treatment = self.parse_null_treatment_modifier();
5805 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5806 && s.eq_ignore_ascii_case("over")
5807 {
5808 self.advance();
5809 let (partition_by, order_by, frame) = self.parse_over_clause()?;
5810 return Ok(Expr::WindowFunction {
5811 name: "count_star".into(),
5812 args: Vec::new(),
5813 partition_by,
5814 order_by,
5815 frame,
5816 null_treatment,
5817 });
5818 }
5819 return Ok(Expr::FunctionCall {
5820 name: "count_star".into(),
5821 args: Vec::new(),
5822 });
5823 }
5824 let mut args = Vec::new();
5826 if !matches!(self.peek(), Token::RParen) {
5827 loop {
5828 args.push(self.parse_expr(0)?);
5829 match self.peek() {
5830 Token::Comma => {
5831 self.advance();
5832 }
5833 Token::RParen => break,
5834 other => {
5835 return Err(self.err(format!(
5836 "expected ',' or ')' in function args, got {other:?}"
5837 )));
5838 }
5839 }
5840 }
5841 }
5842 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
5850 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5851 && s.eq_ignore_ascii_case("over")
5852 {
5853 self.advance();
5854 let (partition_by, order_by, frame) = self.parse_over_clause()?;
5855 return Ok(Expr::WindowFunction {
5856 name: first,
5857 args,
5858 partition_by,
5859 order_by,
5860 frame,
5861 null_treatment,
5862 });
5863 }
5864 return Ok(Expr::FunctionCall { name: first, args });
5865 }
5866 let lc = first.to_ascii_lowercase();
5872 if matches!(
5873 lc.as_str(),
5874 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
5875 ) {
5876 return Ok(Expr::FunctionCall {
5877 name: lc,
5878 args: Vec::new(),
5879 });
5880 }
5881 Ok(Expr::Column(ColumnName {
5882 qualifier: None,
5883 name: first,
5884 }))
5885 }
5886}
5887
5888fn extract_first_column(expr: &Expr) -> Option<String> {
5896 match expr {
5897 Expr::Column(cn) => Some(cn.name.clone()),
5898 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
5899 Expr::Binary { lhs, rhs, .. } => {
5900 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
5901 }
5902 Expr::Unary { expr: e, .. } => extract_first_column(e),
5903 _ => None,
5904 }
5905}
5906
5907fn maybe_not(expr: Expr, negated: bool) -> Expr {
5908 if negated {
5909 Expr::Unary {
5910 op: UnOp::Not,
5911 expr: Box::new(expr),
5912 }
5913 } else {
5914 expr
5915 }
5916}
5917
5918fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
5919 let pair = match tok {
5920 Token::Or => (BinOp::Or, 1),
5921 Token::And => (BinOp::And, 2),
5922 Token::Eq => (BinOp::Eq, 4),
5923 Token::NotEq => (BinOp::NotEq, 4),
5924 Token::Lt => (BinOp::Lt, 4),
5925 Token::LtEq => (BinOp::LtEq, 4),
5926 Token::Gt => (BinOp::Gt, 4),
5927 Token::GtEq => (BinOp::GtEq, 4),
5928 Token::L2Distance => (BinOp::L2Distance, 5),
5931 Token::InnerProduct => (BinOp::InnerProduct, 5),
5932 Token::CosineDistance => (BinOp::CosineDistance, 5),
5933 Token::Plus => (BinOp::Add, 6),
5934 Token::Minus => (BinOp::Sub, 6),
5935 Token::Concat => (BinOp::Concat, 6),
5938 Token::Star => (BinOp::Mul, 7),
5939 Token::Slash => (BinOp::Div, 7),
5940 Token::JsonGet => (BinOp::JsonGet, 7),
5944 Token::JsonGetText => (BinOp::JsonGetText, 7),
5945 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
5946 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
5947 Token::JsonContains => (BinOp::JsonContains, 7),
5948 Token::TsMatch => (BinOp::TsMatch, 4),
5952 _ => return None,
5953 };
5954 Some(pair)
5955}
5956
5957#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
5958fn extract_numeric_literal(e: &Expr) -> Option<f32> {
5963 match e {
5964 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
5965 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
5966 Expr::Unary {
5967 op: UnOp::Neg,
5968 expr,
5969 } => extract_numeric_literal(expr).map(|x| -x),
5970 _ => None,
5971 }
5972}
5973
5974pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
5982 let parts: Vec<&str> = s.split_whitespace().collect();
5983 if parts.is_empty() || !parts.len().is_multiple_of(2) {
5984 return None;
5985 }
5986 let mut months: i32 = 0;
5987 let mut micros: i64 = 0;
5988 let mut i = 0;
5989 while i < parts.len() {
5990 let n: i64 = parts[i].parse().ok()?;
5991 let unit = parts[i + 1].to_ascii_lowercase();
5992 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
5993 match unit_stripped {
5994 "microsecond" => micros = micros.checked_add(n)?,
5995 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
5996 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
5997 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
5998 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
5999 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
6000 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
6001 "month" => {
6002 let n32 = i32::try_from(n).ok()?;
6003 months = months.checked_add(n32)?;
6004 }
6005 "year" => {
6006 let n32 = i32::try_from(n).ok()?;
6007 months = months.checked_add(n32.checked_mul(12)?)?;
6008 }
6009 _ => return None,
6010 }
6011 i += 2;
6012 }
6013 Some((months, micros))
6014}
6015
6016fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
6027 Some(match ident.to_ascii_lowercase().as_str() {
6028 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
6029 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
6030 "bigint" => ColumnTypeName::BigInt,
6031 "float" | "double" | "real" => ColumnTypeName::Float,
6032 "text" => ColumnTypeName::Text,
6033 "bool" | "boolean" => ColumnTypeName::Bool,
6034 "date" => ColumnTypeName::Date,
6035 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
6036 "timestamptz" => ColumnTypeName::Timestamptz,
6037 "json" => ColumnTypeName::Json,
6038 "jsonb" => ColumnTypeName::Jsonb,
6039 "bytea" | "bytes" => ColumnTypeName::Bytes,
6040 "tsvector" => ColumnTypeName::TsVector,
6041 "tsquery" => ColumnTypeName::TsQuery,
6042 _ => return None,
6043 })
6044}
6045
6046pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
6073 parse_plpgsql_body(body)
6074}
6075
6076fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
6077 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
6081 message: alloc::format!("plpgsql body lex error: {e}"),
6082 token_pos: 0,
6083 })?;
6084 let mut parser = Parser::new(tokens);
6085 parser.parse_plpgsql_block()
6086}
6087
6088#[cfg(test)]
6089mod tests {
6090 use super::*;
6091 use alloc::string::ToString;
6092
6093 fn parse(s: &str) -> Statement {
6094 parse_statement(s).expect("parse ok")
6095 }
6096
6097 fn lit_int(n: i64) -> Expr {
6098 Expr::Literal(Literal::Integer(n))
6099 }
6100
6101 fn col(name: &str) -> Expr {
6102 Expr::Column(ColumnName {
6103 qualifier: None,
6104 name: name.into(),
6105 })
6106 }
6107
6108 #[test]
6109 fn select_single_integer() {
6110 let s = parse("SELECT 1");
6111 let Statement::Select(s) = s else {
6112 panic!("expected SELECT")
6113 };
6114 assert_eq!(s.items.len(), 1);
6115 assert!(s.from.is_none());
6116 assert!(s.where_.is_none());
6117 }
6118
6119 #[test]
6120 fn select_multiple_literal_kinds() {
6121 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
6122 let Statement::Select(s) = s else {
6123 panic!("expected SELECT")
6124 };
6125 assert_eq!(s.items.len(), 5);
6126 }
6127
6128 #[test]
6129 fn select_wildcard_from_table() {
6130 let s = parse("SELECT * FROM users");
6131 let Statement::Select(s) = s else {
6132 panic!("expected SELECT")
6133 };
6134 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
6135 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
6136 }
6137
6138 #[test]
6139 fn select_with_table_alias() {
6140 let s = parse("SELECT * FROM users AS u");
6141 let Statement::Select(s) = s else {
6142 panic!("expected SELECT")
6143 };
6144 let t = &s.from.as_ref().unwrap().primary;
6145 assert_eq!(t.name, "users");
6146 assert_eq!(t.alias.as_deref(), Some("u"));
6147 }
6148
6149 #[test]
6150 fn select_with_where_eq() {
6151 let s = parse("SELECT a FROM t WHERE a = 1");
6152 let Statement::Select(s) = s else {
6153 panic!("expected SELECT")
6154 };
6155 let w = s.where_.unwrap();
6156 assert_eq!(
6157 w,
6158 Expr::Binary {
6159 lhs: Box::new(col("a")),
6160 op: BinOp::Eq,
6161 rhs: Box::new(lit_int(1)),
6162 }
6163 );
6164 }
6165
6166 #[test]
6167 fn arithmetic_precedence() {
6168 let s = parse("SELECT 1 + 2 * 3");
6169 let Statement::Select(s) = s else {
6170 panic!("expected SELECT")
6171 };
6172 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6173 panic!("wildcard?")
6174 };
6175 assert_eq!(
6176 expr,
6177 &Expr::Binary {
6178 lhs: Box::new(lit_int(1)),
6179 op: BinOp::Add,
6180 rhs: Box::new(Expr::Binary {
6181 lhs: Box::new(lit_int(2)),
6182 op: BinOp::Mul,
6183 rhs: Box::new(lit_int(3)),
6184 }),
6185 }
6186 );
6187 }
6188
6189 #[test]
6190 fn parentheses_override_precedence() {
6191 let s = parse("SELECT (1 + 2) * 3");
6192 let Statement::Select(s) = s else {
6193 panic!("expected SELECT")
6194 };
6195 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6196 panic!()
6197 };
6198 assert_eq!(
6199 expr,
6200 &Expr::Binary {
6201 lhs: Box::new(Expr::Binary {
6202 lhs: Box::new(lit_int(1)),
6203 op: BinOp::Add,
6204 rhs: Box::new(lit_int(2)),
6205 }),
6206 op: BinOp::Mul,
6207 rhs: Box::new(lit_int(3)),
6208 }
6209 );
6210 }
6211
6212 #[test]
6213 fn not_binds_below_comparison() {
6214 let s = parse("SELECT NOT a = 1 FROM t");
6216 let Statement::Select(s) = s else {
6217 panic!("expected SELECT")
6218 };
6219 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6220 panic!()
6221 };
6222 assert_eq!(
6223 expr,
6224 &Expr::Unary {
6225 op: UnOp::Not,
6226 expr: Box::new(Expr::Binary {
6227 lhs: Box::new(col("a")),
6228 op: BinOp::Eq,
6229 rhs: Box::new(lit_int(1)),
6230 }),
6231 }
6232 );
6233 }
6234
6235 #[test]
6236 fn unary_minus_binds_above_multiplication() {
6237 let s = parse("SELECT -a * 2 FROM t");
6239 let Statement::Select(s) = s else {
6240 panic!("expected SELECT")
6241 };
6242 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6243 panic!()
6244 };
6245 assert_eq!(
6246 expr,
6247 &Expr::Binary {
6248 lhs: Box::new(Expr::Unary {
6249 op: UnOp::Neg,
6250 expr: Box::new(col("a")),
6251 }),
6252 op: BinOp::Mul,
6253 rhs: Box::new(lit_int(2)),
6254 }
6255 );
6256 }
6257
6258 #[test]
6259 fn qualified_column() {
6260 let s = parse("SELECT t.col FROM t");
6261 let Statement::Select(s) = s else {
6262 panic!("expected SELECT")
6263 };
6264 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6265 panic!()
6266 };
6267 assert_eq!(
6268 expr,
6269 &Expr::Column(ColumnName {
6270 qualifier: Some("t".into()),
6271 name: "col".into()
6272 })
6273 );
6274 }
6275
6276 #[test]
6277 fn select_item_alias_with_as() {
6278 let s = parse("SELECT a AS y FROM t");
6279 let Statement::Select(s) = s else {
6280 panic!("expected SELECT")
6281 };
6282 let SelectItem::Expr { alias, .. } = &s.items[0] else {
6283 panic!()
6284 };
6285 assert_eq!(alias.as_deref(), Some("y"));
6286 }
6287
6288 #[test]
6289 fn trailing_semicolon_accepted() {
6290 let s = parse("SELECT 1;");
6291 let Statement::Select(s) = s else {
6292 panic!("expected SELECT")
6293 };
6294 assert_eq!(s.items.len(), 1);
6295 }
6296
6297 #[test]
6298 fn boolean_chain_with_and_or_not() {
6299 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
6301 let Statement::Select(s) = s else {
6302 panic!("expected SELECT")
6303 };
6304 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6305 panic!()
6306 };
6307 let expected = Expr::Binary {
6308 lhs: Box::new(Expr::Unary {
6309 op: UnOp::Not,
6310 expr: Box::new(col("a")),
6311 }),
6312 op: BinOp::Or,
6313 rhs: Box::new(Expr::Binary {
6314 lhs: Box::new(col("b")),
6315 op: BinOp::And,
6316 rhs: Box::new(Expr::Unary {
6317 op: UnOp::Not,
6318 expr: Box::new(col("c")),
6319 }),
6320 }),
6321 };
6322 assert_eq!(expr, &expected);
6323 }
6324
6325 #[test]
6326 fn empty_input_errors() {
6327 assert!(matches!(
6334 parse_statement("").unwrap(),
6335 Statement::Empty
6336 ));
6337 assert!(matches!(
6338 parse_statement(" \n\t ").unwrap(),
6339 Statement::Empty
6340 ));
6341 assert!(parse_statement("SELECT FROM WHERE").is_err());
6343 }
6344
6345 #[test]
6346 fn unmatched_paren_errors() {
6347 assert!(parse_statement("SELECT (1 + 2").is_err());
6348 }
6349
6350 #[test]
6351 fn display_round_trip_simple_select() {
6352 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
6353 let text = original.to_string();
6354 let again = parse_statement(&text).expect("re-parse");
6355 assert_eq!(original, again);
6356 }
6357
6358 #[test]
6361 fn create_table_single_column() {
6362 let s = parse("CREATE TABLE foo (a INT)");
6363 let Statement::CreateTable(c) = s else {
6364 panic!("expected CreateTable")
6365 };
6366 assert_eq!(c.name, "foo");
6367 assert_eq!(c.columns.len(), 1);
6368 assert_eq!(c.columns[0].name, "a");
6369 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6370 assert!(c.columns[0].nullable);
6371 }
6372
6373 #[test]
6374 fn create_table_multi_column_with_not_null_mix() {
6375 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
6376 let Statement::CreateTable(c) = s else {
6377 panic!()
6378 };
6379 assert_eq!(c.columns.len(), 4);
6380 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6381 assert!(!c.columns[0].nullable);
6382 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
6383 assert!(c.columns[1].nullable);
6384 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
6385 assert!(!c.columns[2].nullable);
6386 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
6387 }
6388
6389 #[test]
6390 fn create_table_bigint_supported() {
6391 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
6392 let Statement::CreateTable(c) = s else {
6393 panic!()
6394 };
6395 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
6396 }
6397
6398 #[test]
6399 fn create_table_vector_default_is_f32() {
6400 let s = parse("CREATE TABLE t (v VECTOR(128))");
6401 let Statement::CreateTable(c) = s else {
6402 panic!()
6403 };
6404 assert_eq!(
6405 c.columns[0].ty,
6406 ColumnTypeName::Vector {
6407 dim: 128,
6408 encoding: VecEncoding::F32,
6409 },
6410 );
6411 }
6412
6413 #[test]
6414 fn create_table_vector_using_sq8() {
6415 for sql in [
6418 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
6419 "CREATE TABLE t (v VECTOR(128) using sq8)",
6420 ] {
6421 let s = parse(sql);
6422 let Statement::CreateTable(c) = s else {
6423 panic!()
6424 };
6425 assert_eq!(
6426 c.columns[0].ty,
6427 ColumnTypeName::Vector {
6428 dim: 128,
6429 encoding: VecEncoding::Sq8,
6430 },
6431 "{sql}",
6432 );
6433 }
6434 }
6435
6436 #[test]
6437 fn create_table_vector_using_unknown_errors() {
6438 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
6447 assert!(
6448 err.message.contains("USING")
6449 || err.message.contains("using")
6450 || err.message.contains("')'")
6451 || err.message.contains("','"),
6452 "expected USING/column-list rejection, got: {}",
6453 err.message
6454 );
6455 }
6456
6457 #[test]
6458 fn vector_using_sq8_display_roundtrips() {
6459 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
6462 let Statement::CreateTable(c) = s else {
6463 panic!()
6464 };
6465 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
6466 }
6467
6468 #[test]
6469 fn parser_recognises_placeholders() {
6470 use crate::ast::{Expr, SelectItem, Statement};
6471 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
6473 let Statement::Select(sel) = s else { panic!() };
6474 assert!(matches!(
6475 sel.items[0],
6476 SelectItem::Expr {
6477 expr: Expr::Placeholder(1),
6478 alias: None
6479 }
6480 ));
6481 let SelectItem::Expr {
6483 expr: Expr::Binary { lhs, rhs, .. },
6484 ..
6485 } = &sel.items[1]
6486 else {
6487 panic!()
6488 };
6489 assert!(matches!(**lhs, Expr::Placeholder(2)));
6490 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
6491 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
6493 panic!()
6494 };
6495 assert!(matches!(**rhs, Expr::Placeholder(3)));
6496 }
6497
6498 #[test]
6499 fn parser_rejects_dollar_zero() {
6500 assert!(parse_statement("SELECT $0").is_err());
6502 }
6503
6504 #[test]
6505 fn placeholder_display_roundtrips() {
6506 let s = parse("SELECT $42 FROM t");
6509 let printed = s.to_string();
6510 assert!(printed.contains("$42"));
6511 let again = parse(&printed);
6512 assert_eq!(s, again);
6513 }
6514
6515 #[test]
6516 fn alter_index_rebuild_bare() {
6517 use crate::ast::{AlterIndexTarget, Statement};
6518 let s = parse("ALTER INDEX my_idx REBUILD");
6519 let Statement::AlterIndex(a) = s else {
6520 panic!("expected AlterIndex, got {s:?}")
6521 };
6522 assert_eq!(a.name, "my_idx");
6523 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
6524 }
6525
6526 #[test]
6527 fn alter_index_rebuild_with_encoding() {
6528 use crate::ast::{AlterIndexTarget, Statement};
6529 for (sql, want) in [
6530 (
6531 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
6532 VecEncoding::F32,
6533 ),
6534 (
6535 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
6536 VecEncoding::Sq8,
6537 ),
6538 (
6539 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6540 VecEncoding::F16,
6541 ),
6542 ] {
6543 let s = parse(sql);
6544 let Statement::AlterIndex(a) = s else {
6545 panic!("{sql}: expected AlterIndex")
6546 };
6547 assert_eq!(a.name, "my_idx");
6548 assert_eq!(
6549 a.target,
6550 AlterIndexTarget::Rebuild {
6551 encoding: Some(want)
6552 },
6553 "{sql}"
6554 );
6555 }
6556 }
6557
6558 #[test]
6559 fn alter_index_rebuild_unknown_encoding_errors() {
6560 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
6561 assert!(
6562 err.message.contains("unknown vector encoding"),
6563 "got: {}",
6564 err.message
6565 );
6566 }
6567
6568 #[test]
6569 fn alter_index_rebuild_display_roundtrips() {
6570 for (input, want) in [
6571 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
6572 (
6573 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6574 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6575 ),
6576 (
6577 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6578 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6579 ),
6580 ] {
6581 let s = parse(input);
6582 assert_eq!(s.to_string(), want);
6583 }
6584 }
6585
6586 #[test]
6587 fn create_table_unknown_type_errors() {
6588 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
6591 assert!(err.message.contains("unsupported column type"));
6592 }
6593
6594 #[test]
6595 fn create_table_missing_table_keyword_errors() {
6596 assert!(parse_statement("CREATE x (a INT)").is_err());
6597 }
6598
6599 #[test]
6600 fn insert_single_value() {
6601 let s = parse("INSERT INTO foo VALUES (42)");
6602 let Statement::Insert(i) = s else {
6603 panic!("expected Insert")
6604 };
6605 assert_eq!(i.table, "foo");
6606 assert_eq!(i.rows.len(), 1);
6607 assert_eq!(i.rows[0].len(), 1);
6608 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
6609 }
6610
6611 #[test]
6612 fn insert_multi_value_with_mixed_literals() {
6613 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
6614 let Statement::Insert(i) = s else { panic!() };
6615 assert_eq!(i.rows.len(), 1);
6616 assert_eq!(i.rows[0].len(), 5);
6617 }
6618
6619 #[test]
6620 fn insert_missing_into_errors() {
6621 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
6622 }
6623
6624 #[test]
6625 fn create_table_round_trip() {
6626 let original =
6627 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
6628 let text = original.to_string();
6629 let again = parse_statement(&text).expect("re-parse");
6630 assert_eq!(original, again);
6631 }
6632
6633 #[test]
6634 fn insert_round_trip_with_negation_and_string() {
6635 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
6636 let text = original.to_string();
6637 let again = parse_statement(&text).expect("re-parse");
6638 assert_eq!(original, again);
6639 }
6640
6641 #[test]
6642 fn unknown_keyword_at_statement_start_errors() {
6643 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
6646 assert!(err.message.contains("expected SELECT"));
6647 }
6648
6649 #[test]
6652 fn create_index_basic() {
6653 let s = parse("CREATE INDEX idx_id ON users (id)");
6654 let Statement::CreateIndex(c) = s else {
6655 panic!("expected CreateIndex")
6656 };
6657 assert_eq!(c.name, "idx_id");
6658 assert_eq!(c.table, "users");
6659 assert_eq!(c.column, "id");
6660 }
6661
6662 #[test]
6663 fn create_index_missing_on_errors() {
6664 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
6665 }
6666
6667 #[test]
6668 fn create_index_missing_paren_errors() {
6669 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
6670 }
6671
6672 #[test]
6673 fn create_index_round_trip() {
6674 let original = parse("CREATE INDEX by_name ON users (name)");
6675 let again = parse_statement(&original.to_string()).unwrap();
6676 assert_eq!(original, again);
6677 }
6678
6679 #[test]
6682 fn create_unique_index_basic() {
6683 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
6684 let Statement::CreateIndex(c) = s else {
6685 panic!("expected CreateIndex");
6686 };
6687 assert!(c.is_unique);
6688 assert_eq!(c.column, "a");
6689 assert!(c.partial_predicate.is_none());
6690 }
6691
6692 #[test]
6693 fn create_unique_index_partial() {
6694 let s = parse(
6696 "CREATE UNIQUE INDEX idx_email_templates_user_default \
6697 ON email_templates (user_address) WHERE is_default = true",
6698 );
6699 let Statement::CreateIndex(c) = s else {
6700 panic!("expected CreateIndex");
6701 };
6702 assert!(c.is_unique);
6703 assert_eq!(c.table, "email_templates");
6704 assert_eq!(c.column, "user_address");
6705 assert!(c.partial_predicate.is_some());
6706 }
6707
6708 #[test]
6709 fn create_unique_index_composite_with_predicate() {
6710 let s = parse(
6712 "CREATE UNIQUE INDEX uq_calendar_events_instance \
6713 ON calendar_events (calendar_id, uid, recurrence_id) \
6714 WHERE recurrence_id IS NOT NULL",
6715 );
6716 let Statement::CreateIndex(c) = s else {
6717 panic!("expected CreateIndex");
6718 };
6719 assert!(c.is_unique);
6720 assert_eq!(c.column, "calendar_id");
6721 assert_eq!(
6722 c.extra_columns,
6723 vec!["uid".to_string(), "recurrence_id".to_string()]
6724 );
6725 assert!(c.partial_predicate.is_some());
6726 }
6727
6728 #[test]
6729 fn create_unique_index_using_btree_ok() {
6730 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
6731 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
6732 }
6733
6734 #[test]
6735 fn create_unique_index_using_hnsw_rejected() {
6736 let err =
6737 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
6738 assert!(err.message.contains("UNIQUE"), "{}", err.message);
6739 }
6740
6741 #[test]
6742 fn create_unique_index_round_trip() {
6743 let original = parse(
6744 "CREATE UNIQUE INDEX uq_calendar_events_master \
6745 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
6746 );
6747 let again = parse_statement(&original.to_string()).unwrap();
6748 assert_eq!(original, again);
6749 }
6750
6751 #[test]
6752 fn create_unique_without_index_errors() {
6753 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
6754 assert!(err.message.contains("INDEX"), "{}", err.message);
6755 }
6756
6757 #[test]
6760 fn create_table_bytea_column() {
6761 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
6762 let Statement::CreateTable(c) = s else {
6763 panic!("expected CreateTable");
6764 };
6765 assert_eq!(c.columns.len(), 2);
6766 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
6767 assert!(!c.columns[1].nullable);
6768 }
6769
6770 #[test]
6771 fn create_table_bytes_alias_column() {
6772 let s = parse("CREATE TABLE t (blob BYTES)");
6773 let Statement::CreateTable(c) = s else {
6774 panic!("expected CreateTable");
6775 };
6776 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
6777 }
6778
6779 #[test]
6780 fn bytea_round_trip_display() {
6781 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
6782 let again = parse_statement(&original.to_string()).unwrap();
6783 assert_eq!(original, again);
6784 }
6785
6786 #[test]
6789 fn begin_commit_rollback_parse_as_unit_variants() {
6790 assert_eq!(parse("BEGIN"), Statement::Begin);
6791 assert_eq!(parse("COMMIT"), Statement::Commit);
6792 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
6793 assert_eq!(parse("BEGIN;"), Statement::Begin);
6795 }
6796
6797 #[test]
6800 fn inner_product_binop_parses() {
6801 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
6802 let Statement::Select(s) = s else { panic!() };
6803 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6804 panic!()
6805 };
6806 assert!(matches!(
6807 expr,
6808 Expr::Binary {
6809 op: BinOp::InnerProduct,
6810 ..
6811 }
6812 ));
6813 }
6814
6815 #[test]
6816 fn cosine_distance_binop_parses() {
6817 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
6818 let Statement::Select(s) = s else { panic!() };
6819 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6820 panic!()
6821 };
6822 assert!(matches!(
6823 expr,
6824 Expr::Binary {
6825 op: BinOp::CosineDistance,
6826 ..
6827 }
6828 ));
6829 }
6830
6831 #[test]
6832 fn vector_cast_postfix_wraps_string_literal() {
6833 let s = parse("SELECT '[1,2,3]'::vector FROM t");
6834 let Statement::Select(s) = s else { panic!() };
6835 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6836 panic!()
6837 };
6838 assert!(matches!(
6839 expr,
6840 Expr::Cast {
6841 target: CastTarget::Vector,
6842 ..
6843 }
6844 ));
6845 }
6846
6847 #[test]
6848 fn unsupported_cast_target_errors() {
6849 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
6851 assert!(err.message.contains("unsupported cast target"));
6852 }
6853
6854 #[test]
6855 fn tx_statements_round_trip() {
6856 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
6857 let original = parse(q);
6858 let again = parse_statement(&original.to_string()).unwrap();
6859 assert_eq!(original, again);
6860 }
6861 }
6862
6863 #[test]
6864 fn interval_text_parsing_units() {
6865 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
6867 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
6868 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
6869 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
6870 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
6872 assert_eq!(
6873 parse_interval_text("1 day 2 hours"),
6874 Some((0, 86_400_000_000 + 7_200_000_000))
6875 );
6876 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
6878 assert_eq!(parse_interval_text(""), None);
6880 assert_eq!(parse_interval_text("garbage"), None);
6881 assert_eq!(parse_interval_text("1 fortnight"), None);
6882 assert_eq!(parse_interval_text("1"), None);
6883 }
6884
6885 #[test]
6886 fn interval_literal_roundtrips_via_display() {
6887 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
6888 let s = parsed.to_string();
6889 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
6891 let again = parse_statement(&s).unwrap();
6893 assert_eq!(parsed, again);
6894 }
6895
6896 #[test]
6899 fn parser_recognises_create_publication_bare() {
6900 let s = parse("CREATE PUBLICATION pub_a");
6901 let Statement::CreatePublication(p) = s else {
6902 panic!("expected CreatePublication, got {s:?}")
6903 };
6904 assert_eq!(p.name, "pub_a");
6905 assert_eq!(p.scope, PublicationScope::AllTables);
6906 }
6907
6908 #[test]
6909 fn parser_recognises_create_publication_for_all_tables() {
6910 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
6911 let Statement::CreatePublication(p) = s else {
6912 panic!("expected CreatePublication, got {s:?}")
6913 };
6914 assert_eq!(p.name, "pub_a");
6915 assert_eq!(p.scope, PublicationScope::AllTables);
6916 }
6917
6918 #[test]
6919 fn parser_recognises_drop_publication() {
6920 let s = parse("DROP PUBLICATION pub_a");
6921 let Statement::DropPublication(name) = s else {
6922 panic!("expected DropPublication, got {s:?}")
6923 };
6924 assert_eq!(name, "pub_a");
6925 }
6926
6927 #[test]
6928 fn parser_recognises_for_table_list() {
6929 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
6930 let Statement::CreatePublication(p) = s else {
6931 panic!("expected CreatePublication, got {s:?}")
6932 };
6933 assert_eq!(p.name, "pub_a");
6934 let PublicationScope::ForTables(ts) = p.scope else {
6935 panic!("expected ForTables scope")
6936 };
6937 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
6938 }
6939
6940 #[test]
6941 fn parser_recognises_for_tables_plural() {
6942 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
6944 let Statement::CreatePublication(p) = s else {
6945 panic!("expected CreatePublication, got {s:?}")
6946 };
6947 let PublicationScope::ForTables(ts) = p.scope else {
6948 panic!("expected ForTables")
6949 };
6950 assert_eq!(ts, alloc::vec!["t1", "t2"]);
6951 }
6952
6953 #[test]
6954 fn parser_recognises_for_all_tables_except_list() {
6955 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
6956 let Statement::CreatePublication(p) = s else {
6957 panic!()
6958 };
6959 let PublicationScope::AllTablesExcept(ts) = p.scope else {
6960 panic!("expected AllTablesExcept")
6961 };
6962 assert_eq!(ts, alloc::vec!["t1", "t2"]);
6963 }
6964
6965 #[test]
6966 fn parser_rejects_for_table_with_empty_list() {
6967 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
6969 .expect_err("must error on empty list");
6970 assert!(!err.message.is_empty());
6973 }
6974
6975 #[test]
6976 fn parser_recognises_show_publications() {
6977 let s = parse("SHOW PUBLICATIONS");
6980 assert!(matches!(s, Statement::ShowPublications));
6981 }
6982
6983 #[test]
6986 fn parser_recognises_create_subscription_single_publication() {
6987 let s = parse(
6988 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
6989 );
6990 let Statement::CreateSubscription(c) = s else {
6991 panic!("expected CreateSubscription, got {s:?}")
6992 };
6993 assert_eq!(c.name, "sub_a");
6994 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
6995 assert_eq!(c.publications, alloc::vec!["pub_a"]);
6996 }
6997
6998 #[test]
6999 fn parser_recognises_create_subscription_multi_publication() {
7000 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
7001 let Statement::CreateSubscription(c) = s else {
7002 panic!()
7003 };
7004 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
7005 }
7006
7007 #[test]
7008 fn parser_rejects_create_subscription_missing_connection() {
7009 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
7010 .expect_err("must error on missing CONNECTION");
7011 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
7012 }
7013
7014 #[test]
7015 fn parser_rejects_create_subscription_missing_publication() {
7016 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
7017 .expect_err("must error on missing PUBLICATION");
7018 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
7019 }
7020
7021 #[test]
7022 fn parser_recognises_drop_subscription() {
7023 let s = parse("DROP SUBSCRIPTION sub_a");
7024 let Statement::DropSubscription(name) = s else {
7025 panic!("expected DropSubscription, got {s:?}")
7026 };
7027 assert_eq!(name, "sub_a");
7028 }
7029
7030 #[test]
7031 fn parser_recognises_show_subscriptions() {
7032 let s = parse("SHOW SUBSCRIPTIONS");
7033 assert!(matches!(s, Statement::ShowSubscriptions));
7034 }
7035
7036 #[test]
7037 fn parser_recognises_wait_for_wal_position_no_timeout() {
7038 let s = parse("WAIT FOR WAL POSITION 12345");
7039 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
7040 panic!("expected WaitForWalPosition, got {s:?}")
7041 };
7042 assert_eq!(pos, 12345);
7043 assert!(timeout_ms.is_none());
7044 }
7045
7046 #[test]
7047 fn parser_recognises_wait_for_wal_position_with_timeout() {
7048 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
7049 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
7050 panic!()
7051 };
7052 assert_eq!(pos, 67890);
7053 assert_eq!(timeout_ms, Some(5000));
7054 }
7055
7056 #[test]
7057 fn parser_rejects_wait_with_negative_position() {
7058 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
7064 assert!(!err.message.is_empty());
7065 }
7066
7067 #[test]
7068 fn parser_recognises_bare_analyze() {
7069 let s = parse("ANALYZE");
7070 assert!(matches!(s, Statement::Analyze(None)));
7071 }
7072
7073 #[test]
7074 fn parser_recognises_analyze_with_table() {
7075 let s = parse("ANALYZE users");
7076 let Statement::Analyze(Some(name)) = s else {
7077 panic!("expected Analyze, got {s:?}")
7078 };
7079 assert_eq!(name, "users");
7080 }
7081
7082 #[test]
7083 fn parser_recognises_analyze_with_quoted_table() {
7084 let s = parse("ANALYZE \"Mixed Case\"");
7085 let Statement::Analyze(Some(name)) = s else {
7086 panic!()
7087 };
7088 assert_eq!(name, "Mixed Case");
7089 }
7090
7091 #[test]
7092 fn parser_rejects_analyze_with_garbage_token() {
7093 let err = parse_statement("ANALYZE 42").expect_err("must error");
7094 assert!(!err.message.is_empty());
7095 }
7096
7097 #[test]
7098 fn analyze_display_roundtrips() {
7099 for sql in ["ANALYZE", "ANALYZE users"] {
7100 let s = parse(sql);
7101 let printed = s.to_string();
7102 let again = parse_statement(&printed)
7103 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7104 assert_eq!(s, again);
7105 }
7106 }
7107
7108 #[test]
7109 fn wait_for_display_roundtrips() {
7110 for sql in [
7111 "WAIT FOR WAL POSITION 12345",
7112 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
7113 ] {
7114 let s = parse(sql);
7115 let printed = s.to_string();
7116 let again = parse_statement(&printed)
7117 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7118 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7119 }
7120 }
7121
7122 #[test]
7123 fn subscription_ddl_display_roundtrips() {
7124 for sql in [
7125 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
7126 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
7127 "DROP SUBSCRIPTION sub_a",
7128 "SHOW SUBSCRIPTIONS",
7129 ] {
7130 let s = parse(sql);
7131 let printed = s.to_string();
7132 let again = parse_statement(&printed)
7133 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7134 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7135 }
7136 }
7137
7138 #[test]
7139 fn parser_drop_dispatches_user_vs_publication() {
7140 let s = parse("DROP USER 'alice'");
7143 let Statement::DropUser(name) = s else {
7144 panic!("expected DropUser, got {s:?}")
7145 };
7146 assert_eq!(name, "alice");
7147 let s = parse("DROP PUBLICATION p1");
7149 assert!(matches!(s, Statement::DropPublication(_)));
7150 }
7151
7152 #[test]
7153 fn publication_ddl_display_roundtrips() {
7154 for sql in [
7157 "CREATE PUBLICATION pub_a",
7158 "CREATE PUBLICATION pub_a FOR ALL TABLES",
7159 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
7160 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
7161 "DROP PUBLICATION pub_a",
7162 "SHOW PUBLICATIONS",
7163 ] {
7164 let s = parse(sql);
7165 let printed = s.to_string();
7166 let again = parse_statement(&printed)
7167 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7168 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7169 }
7170 }
7171
7172 #[test]
7175 fn create_function_returns_trigger_plpgsql_minimal() {
7176 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
7177 let s = parse(sql);
7178 let Statement::CreateFunction(f) = s else {
7179 panic!("expected CreateFunction");
7180 };
7181 assert_eq!(f.name, "noop");
7182 assert!(!f.or_replace);
7183 assert!(f.args.is_empty());
7184 assert!(matches!(f.returns, FunctionReturn::Trigger));
7185 assert_eq!(f.language, "plpgsql");
7186 let FunctionBody::PlPgSql(block) = f.body else {
7187 panic!("expected PlPgSql body");
7188 };
7189 assert_eq!(block.statements.len(), 1);
7190 assert!(matches!(
7191 block.statements[0],
7192 PlPgSqlStmt::Return(ReturnTarget::New)
7193 ));
7194 }
7195
7196 #[test]
7197 fn create_function_or_replace_with_assignment() {
7198 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
7201BEGIN
7202 NEW.search_vector := to_tsvector('english', NEW.subject);
7203 RETURN NEW;
7204END;
7205$$";
7206 let s = parse(sql);
7207 let Statement::CreateFunction(f) = s else {
7208 panic!("expected CreateFunction");
7209 };
7210 assert!(f.or_replace);
7211 let FunctionBody::PlPgSql(block) = &f.body else {
7212 panic!("expected PlPgSql body");
7213 };
7214 assert_eq!(block.statements.len(), 2);
7215 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
7217 panic!("expected Assign as first stmt");
7218 };
7219 match target {
7220 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
7221 other => panic!("expected NEW.col, got {other:?}"),
7222 }
7223 assert!(matches!(
7225 block.statements[1],
7226 PlPgSqlStmt::Return(ReturnTarget::New)
7227 ));
7228 }
7229
7230 #[test]
7231 fn create_trigger_after_insert_or_update() {
7232 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
7233 let s = parse(sql);
7234 let Statement::CreateTrigger(t) = s else {
7235 panic!("expected CreateTrigger");
7236 };
7237 assert_eq!(t.name, "tg");
7238 assert_eq!(t.table, "messages");
7239 assert_eq!(t.timing, TriggerTiming::After);
7240 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
7241 assert_eq!(t.for_each, TriggerForEach::Row);
7242 assert_eq!(t.function, "update_sv");
7243 }
7244
7245 #[test]
7246 fn create_trigger_before_delete_execute_procedure_alias() {
7247 let sql =
7249 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
7250 let s = parse(sql);
7251 let Statement::CreateTrigger(t) = s else {
7252 panic!("expected CreateTrigger");
7253 };
7254 assert_eq!(t.timing, TriggerTiming::Before);
7255 assert_eq!(t.events, vec![TriggerEvent::Delete]);
7256 }
7257
7258 #[test]
7259 fn drop_trigger_if_exists_round_trips() {
7260 let s = Statement::DropTrigger {
7265 name: "tg".into(),
7266 table: "messages".into(),
7267 if_exists: true,
7268 };
7269 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
7270 }
7271
7272 #[test]
7273 fn trigger_ddl_display_roundtrips_through_parser() {
7274 for sql in [
7278 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
7279 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
7280 ] {
7281 let s = parse(sql);
7282 let printed = s.to_string();
7283 let again = parse_statement(&printed)
7284 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7285 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7286 }
7287 }
7288}