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 = self.expect_ident_like()?;
1606 if matches!(self.peek(), Token::Dot) {
1607 self.advance();
1608 let col = self.expect_ident_like()?;
1609 if head.eq_ignore_ascii_case("new") {
1610 return Ok(AssignTarget::NewColumn(col));
1611 }
1612 if head.eq_ignore_ascii_case("old") {
1613 return Ok(AssignTarget::OldColumn(col));
1614 }
1615 return Err(self.err(alloc::format!(
1616 "v7.12.4 plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
1617 got {head:?}.<col>"
1618 )));
1619 }
1620 Ok(AssignTarget::Local(head))
1621 }
1622
1623 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1624 match self.peek() {
1626 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
1627 self.advance();
1628 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
1629 }
1630 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
1631 self.advance();
1632 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
1633 }
1634 Token::Null => {
1635 self.advance();
1636 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1637 }
1638 Token::Semicolon => {
1641 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1642 }
1643 _ => {}
1644 }
1645 let e = self.parse_expr(0)?;
1647 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
1648 }
1649
1650 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
1651 if matches!(self.peek(), Token::Insert) {
1656 self.advance();
1657 return Ok(TriggerEvent::Insert);
1658 }
1659 match self.peek() {
1660 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1661 self.advance();
1662 Ok(TriggerEvent::Update)
1663 }
1664 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1665 self.advance();
1666 Ok(TriggerEvent::Delete)
1667 }
1668 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
1669 self.advance();
1670 Ok(TriggerEvent::Truncate)
1671 }
1672 other => Err(self.err(alloc::format!(
1673 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
1674 ))),
1675 }
1676 }
1677
1678 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
1685 let name = self.expect_ident_or_string()?;
1686 let scope = if matches!(self.peek(), Token::For) {
1689 self.advance();
1690 if matches!(self.peek(), Token::All) {
1691 self.advance();
1692 if !matches!(self.peek(), Token::Tables) {
1693 return Err(self.err(format!(
1694 "expected TABLES after FOR ALL, got {:?}",
1695 self.peek()
1696 )));
1697 }
1698 self.advance();
1699 if matches!(self.peek(), Token::Except) {
1700 self.advance();
1701 let tables = self.parse_publication_table_list()?;
1702 PublicationScope::AllTablesExcept(tables)
1703 } else {
1704 PublicationScope::AllTables
1705 }
1706 } else if matches!(self.peek(), Token::Table | Token::Tables) {
1707 self.advance();
1710 let tables = self.parse_publication_table_list()?;
1711 PublicationScope::ForTables(tables)
1712 } else {
1713 return Err(self.err(format!(
1714 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
1715 self.peek()
1716 )));
1717 }
1718 } else {
1719 PublicationScope::AllTables
1720 };
1721 Ok(Statement::CreatePublication(CreatePublicationStatement {
1722 name,
1723 scope,
1724 }))
1725 }
1726
1727 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
1732 let first = self.expect_ident_like()?;
1733 let mut out = alloc::vec![first];
1734 while matches!(self.peek(), Token::Comma) {
1735 self.advance();
1736 out.push(self.expect_ident_like()?);
1737 }
1738 Ok(out)
1739 }
1740
1741 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
1749 let name = self.expect_ident_or_string()?;
1750 if !matches!(self.peek(), Token::Connection) {
1751 return Err(self.err(format!(
1752 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
1753 self.peek()
1754 )));
1755 }
1756 self.advance();
1757 let conn_str = self.expect_string_literal()?;
1758 if !matches!(self.peek(), Token::Publication) {
1759 return Err(self.err(format!(
1760 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
1761 self.peek()
1762 )));
1763 }
1764 self.advance();
1765 let first = self.expect_ident_like()?;
1768 let mut publications = alloc::vec![first];
1769 while matches!(self.peek(), Token::Comma) {
1770 self.advance();
1771 publications.push(self.expect_ident_like()?);
1772 }
1773 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
1774 name,
1775 conn_str,
1776 publications,
1777 }))
1778 }
1779
1780 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
1787 let mut name = self.expect_ident_like()?;
1788 while matches!(self.peek(), Token::Dot) {
1789 self.advance();
1790 let next = self.expect_ident_like()?;
1791 name.push('.');
1792 name.push_str(&next);
1793 }
1794 Ok(name.to_ascii_lowercase())
1795 }
1796
1797 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
1798 match self.advance() {
1799 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
1800 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
1801 Ok(crate::ast::SetValue::Default)
1802 }
1803 Token::Ident(s) | Token::QuotedIdent(s) => {
1804 let mut accum = s;
1805 while matches!(self.peek(), Token::Dot) {
1806 self.advance();
1807 let next = self.expect_ident_like()?;
1808 accum.push('.');
1809 accum.push_str(&next);
1810 }
1811 Ok(crate::ast::SetValue::Ident(accum))
1812 }
1813 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
1814 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
1815 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
1821 Token::Minus => match self.advance() {
1826 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
1827 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
1828 other => Err(self.err(format!(
1829 "expected numeric after `-` in SET value, got {other:?}"
1830 ))),
1831 },
1832 other => Err(self.err(format!(
1833 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
1834 ))),
1835 }
1836 }
1837
1838 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
1839 if !matches!(self.peek(), Token::For) {
1843 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
1844 }
1845 self.advance();
1846 self.expect_keyword_ident("wal")?;
1847 self.expect_keyword_ident("position")?;
1848 let pos = self.expect_u64_literal()?;
1849 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
1850 {
1851 self.advance();
1852 self.expect_keyword_ident("timeout")?;
1853 Some(self.expect_u64_literal()?)
1854 } else {
1855 None
1856 };
1857 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
1858 }
1859
1860 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
1864 match self.advance() {
1865 Token::Integer(n) if n >= 0 => Ok(n as u64),
1866 Token::Integer(n) => Err(ParseError {
1867 message: format!("expected non-negative integer, got {n}"),
1868 token_pos: self.pos.saturating_sub(1),
1869 }),
1870 other => Err(ParseError {
1871 message: format!("expected integer literal, got {other:?}"),
1872 token_pos: self.pos.saturating_sub(1),
1873 }),
1874 }
1875 }
1876
1877 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
1881 let name = self.expect_ident_or_string()?;
1882 self.expect_keyword_ident("with")?;
1883 self.expect_keyword_ident("password")?;
1884 let password = self.expect_string_literal()?;
1885 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
1886 && s.eq_ignore_ascii_case("role")
1887 {
1888 self.advance();
1889 self.expect_string_literal()?
1890 } else {
1891 "readonly".to_string()
1892 };
1893 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
1894 name,
1895 password,
1896 role,
1897 }))
1898 }
1899
1900 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
1903 let table = self.expect_ident_like()?;
1904 self.expect_keyword_ident("set")?;
1905 let mut assignments = Vec::new();
1906 loop {
1907 let col = self.expect_ident_like()?;
1908 if !matches!(self.peek(), Token::Eq) {
1909 return Err(self.err(format!(
1910 "expected `=` after column name in UPDATE SET, got {:?}",
1911 self.peek()
1912 )));
1913 }
1914 self.advance();
1915 let value = self.parse_expr(0)?;
1916 assignments.push((col, value));
1917 if matches!(self.peek(), Token::Comma) {
1918 self.advance();
1919 continue;
1920 }
1921 break;
1922 }
1923 let where_ = if matches!(self.peek(), Token::Where) {
1924 self.advance();
1925 Some(self.parse_expr(0)?)
1926 } else {
1927 None
1928 };
1929 let returning = self.parse_optional_returning()?;
1930 Ok(Statement::Update(crate::ast::UpdateStatement {
1931 table,
1932 assignments,
1933 where_,
1934 returning,
1935 }))
1936 }
1937
1938 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
1941 if !matches!(self.peek(), Token::From) {
1942 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
1943 }
1944 self.advance();
1945 let table = self.expect_ident_like()?;
1946 let where_ = if matches!(self.peek(), Token::Where) {
1947 self.advance();
1948 Some(self.parse_expr(0)?)
1949 } else {
1950 None
1951 };
1952 let returning = self.parse_optional_returning()?;
1953 Ok(Statement::Delete(crate::ast::DeleteStatement {
1954 table,
1955 where_,
1956 returning,
1957 }))
1958 }
1959
1960 fn parse_optional_returning(
1965 &mut self,
1966 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
1967 let is_returning_kw = matches!(
1968 self.peek(),
1969 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
1970 );
1971 if !is_returning_kw {
1972 return Ok(None);
1973 }
1974 self.advance();
1975 let mut items = Vec::new();
1976 loop {
1977 items.push(self.parse_select_item()?);
1978 if matches!(self.peek(), Token::Comma) {
1979 self.advance();
1980 continue;
1981 }
1982 break;
1983 }
1984 Ok(Some(items))
1985 }
1986
1987 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
1995 match self.advance() {
2002 Token::Index => {}
2003 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
2004 Token::Table => {
2007 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2008 self.advance();
2009 }
2010 return self.parse_alter_table_after_keyword();
2011 }
2012 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
2013 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2014 self.advance();
2015 }
2016 return self.parse_alter_table_after_keyword();
2017 }
2018 Token::Ident(s) | Token::QuotedIdent(s)
2024 if matches!(
2025 s.to_ascii_lowercase().as_str(),
2026 "sequence"
2027 | "view"
2028 | "function"
2029 | "type"
2030 | "domain"
2031 | "database"
2032 | "role"
2033 | "schema"
2034 | "owner"
2035 | "default"
2036 | "extension"
2037 | "materialized"
2038 | "policy"
2039 | "publication"
2040 | "subscription"
2041 ) =>
2042 {
2043 self.consume_until_statement_boundary();
2044 return Ok(Statement::Empty);
2045 }
2046 other => {
2047 return Err(self.err(format!(
2048 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
2049 after ALTER, got {other:?}"
2050 )));
2051 }
2052 }
2053 let name = self.expect_ident_like()?;
2054 self.expect_keyword_ident("rebuild")?;
2056 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2058 self.advance();
2059 if !matches!(self.peek(), Token::LParen) {
2060 return Err(self.err(format!(
2061 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
2062 self.peek()
2063 )));
2064 }
2065 self.advance();
2066 self.expect_keyword_ident("encoding")?;
2067 if !matches!(self.peek(), Token::Eq) {
2068 return Err(self.err(format!(
2069 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
2070 self.peek()
2071 )));
2072 }
2073 self.advance();
2074 let enc_ident = match self.advance() {
2075 Token::Ident(s) | Token::QuotedIdent(s) => s,
2076 other => {
2077 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
2078 }
2079 };
2080 let enc = match enc_ident.to_ascii_lowercase().as_str() {
2081 "f32" => VecEncoding::F32,
2082 "sq8" => VecEncoding::Sq8,
2083 "half" => VecEncoding::F16,
2084 other => {
2085 return Err(self.err(format!(
2086 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
2087 )));
2088 }
2089 };
2090 if !matches!(self.peek(), Token::RParen) {
2091 return Err(self.err(format!(
2092 "expected ')' after encoding value, got {:?}",
2093 self.peek()
2094 )));
2095 }
2096 self.advance();
2097 Some(enc)
2098 } else {
2099 None
2100 };
2101 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
2102 name,
2103 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
2104 }))
2105 }
2106
2107 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
2113 let table_name = self.expect_ident_like()?;
2114 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
2115 loop {
2116 let subaction = self.parse_alter_table_subaction()?;
2117 targets.extend(subaction);
2121 if matches!(self.peek(), Token::Comma) {
2122 self.advance();
2123 continue;
2124 }
2125 break;
2126 }
2127 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
2128 name: table_name,
2129 targets,
2130 }))
2131 }
2132
2133 fn parse_alter_table_subaction(
2137 &mut self,
2138 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
2139 match self.peek() {
2140 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2141 self.advance();
2142 let setting = self.expect_ident_like()?;
2143 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
2144 return Err(self.err(alloc::format!(
2145 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
2146 )));
2147 }
2148 if !matches!(self.peek(), Token::Eq) {
2149 return Err(self.err(alloc::format!(
2150 "expected '=' after hot_tier_bytes, got {:?}",
2151 self.peek()
2152 )));
2153 }
2154 self.advance();
2155 let n = self.expect_u64_literal()?;
2156 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
2157 }
2158 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
2159 self.advance();
2160 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
2167 {
2168 let kind_pos = self.pos + 2;
2171 let kind = self.tokens.get(kind_pos).cloned();
2172 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
2173 {
2174 let fk = self.parse_table_level_fk()?;
2175 return Ok(alloc::vec![
2176 crate::ast::AlterTableTarget::AddForeignKey(fk)
2177 ]);
2178 }
2179 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
2180 {
2181 self.advance(); let _name = self.expect_ident_like()?;
2183 self.advance(); self.expect_keyword_ident("key")?;
2185 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2186 return Ok(alloc::vec![
2187 crate::ast::AlterTableTarget::AddTableConstraint(
2188 crate::ast::TableConstraint::PrimaryKey {
2189 name: None,
2190 columns: cols,
2191 }
2192 )
2193 ]);
2194 }
2195 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
2196 {
2197 self.advance(); let _name = self.expect_ident_like()?;
2199 self.advance(); let cols = self.parse_paren_ident_list("UNIQUE")?;
2201 return Ok(alloc::vec![
2202 crate::ast::AlterTableTarget::AddTableConstraint(
2203 crate::ast::TableConstraint::Unique {
2204 name: None,
2205 columns: cols,
2206 nulls_not_distinct: false,
2207 }
2208 )
2209 ]);
2210 }
2211 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
2212 {
2213 self.advance(); let _name = self.expect_ident_like()?;
2215 self.advance(); if !matches!(self.peek(), Token::LParen) {
2217 return Err(self.err(alloc::format!(
2218 "expected '(' after CHECK, got {:?}", self.peek()
2219 )));
2220 }
2221 self.advance();
2222 let expr = self.parse_expr(0)?;
2223 if matches!(self.peek(), Token::RParen) {
2224 self.advance();
2225 }
2226 return Ok(alloc::vec![
2227 crate::ast::AlterTableTarget::AddTableConstraint(
2228 crate::ast::TableConstraint::Check { name: None, expr }
2229 )
2230 ]);
2231 }
2232 }
2235 let is_fk = matches!(
2236 self.peek(),
2237 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2238 || s.eq_ignore_ascii_case("foreign")
2239 );
2240 if is_fk {
2241 let fk = self.parse_table_level_fk()?;
2242 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
2243 }
2244 match self.peek().clone() {
2247 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
2248 self.advance();
2249 self.expect_keyword_ident("key")?;
2250 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2251 return Ok(alloc::vec![
2252 crate::ast::AlterTableTarget::AddTableConstraint(
2253 crate::ast::TableConstraint::PrimaryKey {
2254 name: None,
2255 columns: cols,
2256 }
2257 )
2258 ]);
2259 }
2260 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
2261 self.advance();
2262 let cols = self.parse_paren_ident_list("UNIQUE")?;
2263 return Ok(alloc::vec![
2264 crate::ast::AlterTableTarget::AddTableConstraint(
2265 crate::ast::TableConstraint::Unique {
2266 name: None,
2267 columns: cols,
2268 nulls_not_distinct: false,
2269 }
2270 )
2271 ]);
2272 }
2273 _ => {}
2274 }
2275 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2276 self.advance();
2277 }
2278 let mut if_not_exists = false;
2279 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2280 self.advance();
2281 if !matches!(self.peek(), Token::Not) {
2282 return Err(self.err(alloc::format!(
2283 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
2284 self.peek()
2285 )));
2286 }
2287 self.advance();
2288 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
2289 return Err(self.err(alloc::format!(
2290 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
2291 self.peek()
2292 )));
2293 }
2294 self.advance();
2295 if_not_exists = true;
2296 }
2297 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
2301 let col_name = column.name.clone();
2302 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
2303 column,
2304 if_not_exists,
2305 }];
2306 if let Some(mut fk) = col_level_fk {
2307 if fk.columns.is_empty() {
2308 fk.columns.push(col_name);
2309 }
2310 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
2311 }
2312 Ok(out)
2313 }
2314 Token::Drop => {
2315 self.advance();
2316 let subject = match self.peek() {
2323 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
2324 self.advance();
2325 "constraint"
2326 }
2327 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
2328 self.advance();
2329 "column"
2330 }
2331 Token::Ident(_) | Token::QuotedIdent(_) => "column",
2335 other => {
2336 return Err(self.err(alloc::format!(
2337 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
2338 )));
2339 }
2340 };
2341 let mut if_exists = false;
2342 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2343 let n1 = self.tokens.get(self.pos + 1);
2344 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
2345 self.advance();
2346 self.advance();
2347 if_exists = true;
2348 }
2349 }
2350 let name = self.expect_ident_like()?;
2351 let mut cascade = false;
2352 if matches!(
2353 self.peek(),
2354 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
2355 || s.eq_ignore_ascii_case("restrict")
2356 ) {
2357 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
2358 {
2359 cascade = true;
2360 }
2361 self.advance();
2362 }
2363 if subject == "constraint" {
2364 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
2365 name,
2366 if_exists,
2367 }])
2368 } else {
2369 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
2370 column: name,
2371 if_exists,
2372 cascade,
2373 }])
2374 }
2375 }
2376 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
2377 self.advance();
2378 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2379 self.advance();
2380 }
2381 let col_name = self.expect_ident_like()?;
2382 match self.peek() {
2383 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
2384 self.advance();
2385 }
2386 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2394 self.consume_until_statement_boundary();
2399 return Ok(Vec::new());
2400 }
2401 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
2402 self.consume_until_statement_boundary();
2404 return Ok(Vec::new());
2405 }
2406 other => {
2407 return Err(self.err(alloc::format!(
2408 "expected TYPE / SET / DROP after ALTER COLUMN <name>, got {other:?}"
2409 )));
2410 }
2411 }
2412 let new_type = self.parse_column_type_name()?;
2413 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
2414 {
2415 self.advance();
2416 Some(self.parse_expr(0)?)
2417 } else {
2418 None
2419 };
2420 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
2421 column: col_name,
2422 new_type,
2423 using,
2424 }])
2425 }
2426 other => Err(self.err(alloc::format!(
2427 "expected SET / ADD / DROP / ALTER in ALTER TABLE, got {other:?}"
2428 ))),
2429 }
2430 }
2431
2432 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
2434 match self.advance() {
2435 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
2436 other => Err(ParseError {
2437 message: format!("expected {kw:?}, got {other:?}"),
2438 token_pos: self.pos.saturating_sub(1),
2439 }),
2440 }
2441 }
2442
2443 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
2447 match self.advance() {
2448 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
2449 other => Err(ParseError {
2450 message: format!("expected identifier or string, got {other:?}"),
2451 token_pos: self.pos.saturating_sub(1),
2452 }),
2453 }
2454 }
2455
2456 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
2457 match self.advance() {
2458 Token::String(s) => Ok(s),
2459 other => Err(ParseError {
2460 message: format!("expected quoted string, got {other:?}"),
2461 token_pos: self.pos.saturating_sub(1),
2462 }),
2463 }
2464 }
2465
2466 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
2467 let mut head = self.parse_bare_select()?;
2472 while matches!(self.peek(), Token::Union) {
2473 self.advance();
2474 let kind = if matches!(self.peek(), Token::All) {
2475 self.advance();
2476 UnionKind::All
2477 } else {
2478 UnionKind::Distinct
2479 };
2480 let peer = self.parse_bare_select()?;
2481 head.unions.push((kind, peer));
2482 }
2483 head.order_by = if matches!(self.peek(), Token::Order) {
2484 self.advance();
2485 if !matches!(self.peek(), Token::By) {
2486 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
2487 }
2488 self.advance();
2489 let mut keys = Vec::new();
2492 loop {
2493 let expr = self.parse_expr(0)?;
2494 let desc = if matches!(self.peek(), Token::Desc) {
2495 self.advance();
2496 true
2497 } else if matches!(self.peek(), Token::Asc) {
2498 self.advance();
2499 false
2500 } else {
2501 false
2502 };
2503 keys.push(OrderBy { expr, desc });
2504 if matches!(self.peek(), Token::Comma) {
2505 self.advance();
2506 } else {
2507 break;
2508 }
2509 }
2510 keys
2511 } else {
2512 Vec::new()
2513 };
2514 head.limit = if matches!(self.peek(), Token::Limit) {
2515 self.advance();
2516 Some(self.parse_limit_expr("LIMIT")?)
2517 } else {
2518 None
2519 };
2520 head.offset = if matches!(self.peek(), Token::Offset) {
2521 self.advance();
2522 Some(self.parse_limit_expr("OFFSET")?)
2523 } else {
2524 None
2525 };
2526 Ok(Statement::Select(head))
2527 }
2528
2529 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2534 match self.advance() {
2535 Token::Integer(n) if n >= 0 => u32::try_from(n)
2536 .map(crate::ast::LimitExpr::Literal)
2537 .map_err(|_| ParseError {
2538 message: alloc::format!("{label} value too large: {n}"),
2539 token_pos: self.pos.saturating_sub(1),
2540 }),
2541 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2542 other => Err(ParseError {
2543 message: alloc::format!(
2544 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2545 ),
2546 token_pos: self.pos.saturating_sub(1),
2547 }),
2548 }
2549 }
2550
2551 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2556 if !matches!(self.peek(), Token::Select) {
2557 return Err(self.err(format!(
2558 "expected SELECT to start a query block, got {:?}",
2559 self.peek()
2560 )));
2561 }
2562 self.advance();
2563 let distinct = if matches!(self.peek(), Token::Distinct) {
2564 self.advance();
2565 true
2566 } else {
2567 false
2568 };
2569 let items = self.parse_select_list()?;
2570 let from = if matches!(self.peek(), Token::From) {
2571 self.advance();
2572 Some(self.parse_from_clause()?)
2573 } else {
2574 None
2575 };
2576 let where_ = if matches!(self.peek(), Token::Where) {
2577 self.advance();
2578 Some(self.parse_expr(0)?)
2579 } else {
2580 None
2581 };
2582 let mut group_by_all = false;
2583 let group_by = if matches!(self.peek(), Token::Group) {
2584 self.advance();
2585 if !matches!(self.peek(), Token::By) {
2586 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2587 }
2588 self.advance();
2589 if matches!(self.peek(), Token::All) {
2592 self.advance();
2593 group_by_all = true;
2594 None
2595 } else {
2596 let mut groups = Vec::new();
2597 loop {
2598 groups.push(self.parse_expr(0)?);
2599 if matches!(self.peek(), Token::Comma) {
2600 self.advance();
2601 } else {
2602 break;
2603 }
2604 }
2605 Some(groups)
2606 }
2607 } else {
2608 None
2609 };
2610 let having = if matches!(self.peek(), Token::Having) {
2611 self.advance();
2612 Some(self.parse_expr(0)?)
2613 } else {
2614 None
2615 };
2616 Ok(SelectStatement {
2617 ctes: Vec::new(),
2618 distinct,
2619 items,
2620 from,
2621 where_,
2622 group_by,
2623 group_by_all,
2624 having,
2625 unions: Vec::new(),
2626 order_by: Vec::new(),
2627 limit: None,
2628 offset: None,
2629 })
2630 }
2631
2632 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2633 debug_assert!(matches!(self.peek(), Token::Table));
2635 self.advance();
2636 let if_not_exists = self.consume_if_not_exists();
2637 let name = self.expect_ident_like()?;
2638 if !matches!(self.peek(), Token::LParen) {
2639 return Err(self.err(format!(
2640 "expected '(' after table name, got {:?}",
2641 self.peek()
2642 )));
2643 }
2644 self.advance();
2645 let mut columns = Vec::new();
2646 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
2647 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
2648 loop {
2649 if self.peek_table_level_pk_start() {
2655 table_constraints.push(self.parse_table_level_primary_key()?);
2656 } else if self.peek_table_level_unique_start() {
2657 table_constraints.push(self.parse_table_level_unique()?);
2658 } else if self.peek_table_level_check_start() {
2659 table_constraints.push(self.parse_table_level_check()?);
2661 } else if self.peek_mysql_inline_key_start() {
2662 if let Some(uc) = self.parse_mysql_inline_key()? {
2668 table_constraints.push(uc);
2669 }
2670 } else if self.peek_constraint_or_fk_start() {
2671 foreign_keys.push(self.parse_table_level_fk()?);
2672 } else {
2673 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
2674 if col.is_unique {
2678 table_constraints.push(crate::ast::TableConstraint::Unique {
2679 name: None,
2680 columns: alloc::vec![col.name.clone()],
2681 nulls_not_distinct: false,
2682 });
2683 }
2684 if let Some(check_expr) = col.check.clone() {
2685 table_constraints.push(crate::ast::TableConstraint::Check {
2686 name: None,
2687 expr: check_expr,
2688 });
2689 }
2690 columns.push(col);
2691 if let Some(fk) = col_level_fk {
2692 foreign_keys.push(fk);
2693 }
2694 }
2695 match self.peek() {
2696 Token::Comma => {
2697 self.advance();
2698 }
2699 Token::RParen => {
2700 self.advance();
2701 break;
2702 }
2703 other => {
2704 return Err(
2705 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2706 );
2707 }
2708 }
2709 }
2710 if columns.is_empty() {
2711 return Err(self.err("CREATE TABLE requires at least one column".into()));
2712 }
2713 self.consume_mysql_table_options();
2720 Ok(Statement::CreateTable(CreateTableStatement {
2721 name,
2722 columns,
2723 if_not_exists,
2724 foreign_keys,
2725 table_constraints,
2726 }))
2727 }
2728
2729 fn peek_mysql_inline_key_start(&self) -> bool {
2738 let cur = self.peek();
2739 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
2749 match self.tokens.get(skip) {
2752 Some(Token::LParen) => true,
2753 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
2754 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
2755 }
2756 _ => false,
2757 }
2758 };
2759 match cur {
2760 Token::Ident(s)
2761 if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") =>
2762 {
2763 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
2764 }
2765 Token::Ident(s)
2766 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
2767 {
2768 let nxt = self.tokens.get(self.pos + 1);
2769 let after_after = if matches!(
2770 nxt,
2771 Some(Token::Ident(t))
2772 if t.eq_ignore_ascii_case("key") || t.eq_ignore_ascii_case("index")
2773 ) {
2774 self.pos + 2
2775 } else {
2776 self.pos + 1
2777 };
2778 after_keyword_followed_by_paren_or_ident_paren(after_after)
2779 }
2780 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
2781 let nxt = self.tokens.get(self.pos + 1);
2782 if !matches!(
2783 nxt,
2784 Some(Token::Ident(t))
2785 if t.eq_ignore_ascii_case("key") || t.eq_ignore_ascii_case("index")
2786 ) {
2787 return false;
2788 }
2789 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
2790 }
2791 _ => false,
2792 }
2793 }
2794
2795 fn parse_mysql_inline_key(
2801 &mut self,
2802 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
2803 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
2805 {
2806 self.advance();
2807 true
2808 } else {
2809 false
2810 };
2811 if matches!(
2813 self.peek(),
2814 Token::Ident(s) if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial")
2815 ) {
2816 self.advance();
2817 }
2818 match self.peek() {
2820 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
2821 self.advance();
2822 }
2823 other => {
2824 return Err(self.err(alloc::format!(
2825 "expected KEY/INDEX in inline index declaration, got {other:?}"
2826 )));
2827 }
2828 }
2829 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
2831 && !matches!(self.tokens.get(self.pos + 1), Some(Token::LParen) | None)
2832 {
2833 }
2836 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
2837 && matches!(
2838 self.tokens.get(self.pos + 1),
2839 Some(Token::LParen)
2840 | Some(Token::Ident(_))
2841 | Some(Token::QuotedIdent(_))
2842 )
2843 {
2844 if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) {
2846 self.advance();
2847 }
2848 }
2849 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2851 self.advance();
2852 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2853 self.advance();
2854 }
2855 }
2856 if !matches!(self.peek(), Token::LParen) {
2858 return Err(self.err(alloc::format!(
2859 "expected '(' in inline KEY/INDEX, got {:?}",
2860 self.peek()
2861 )));
2862 }
2863 self.advance();
2864 let mut cols: Vec<String> = Vec::new();
2865 loop {
2866 match self.peek().clone() {
2867 Token::Ident(s) | Token::QuotedIdent(s) => {
2868 self.advance();
2869 cols.push(s);
2870 }
2871 _ => break,
2872 }
2873 if matches!(self.peek(), Token::LParen) {
2875 let mut depth = 1usize;
2876 self.advance();
2877 while depth > 0 {
2878 match self.peek() {
2879 Token::LParen => depth += 1,
2880 Token::RParen => depth -= 1,
2881 Token::Eof => break,
2882 _ => {}
2883 }
2884 self.advance();
2885 }
2886 }
2887 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
2889 || matches!(self.peek(), Token::Asc | Token::Desc)
2890 {
2891 self.advance();
2892 }
2893 if matches!(self.peek(), Token::Comma) {
2894 self.advance();
2895 continue;
2896 }
2897 break;
2898 }
2899 if matches!(self.peek(), Token::RParen) {
2900 self.advance();
2901 }
2902 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
2905 self.advance();
2906 }
2907 if is_unique && !cols.is_empty() {
2908 Ok(Some(crate::ast::TableConstraint::Unique {
2909 name: None,
2910 columns: cols,
2911 nulls_not_distinct: false,
2912 }))
2913 } else {
2914 Ok(None)
2915 }
2916 }
2917
2918 fn consume_mysql_table_options(&mut self) {
2923 loop {
2924 let name_lc = match self.peek().clone() {
2928 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
2929 Token::Default => alloc::string::String::from("default"),
2930 _ => break,
2931 };
2932 let known = matches!(
2933 name_lc.as_str(),
2934 "engine"
2935 | "default"
2936 | "charset"
2937 | "collate"
2938 | "auto_increment"
2939 | "row_format"
2940 | "comment"
2941 | "pack_keys"
2942 | "stats_persistent"
2943 | "stats_auto_recalc"
2944 | "stats_sample_pages"
2945 | "key_block_size"
2946 | "tablespace"
2947 | "min_rows"
2948 | "max_rows"
2949 | "checksum"
2950 | "delay_key_write"
2951 | "insert_method"
2952 | "data"
2953 | "index"
2954 | "encryption"
2955 | "compression"
2956 );
2957 if !known {
2958 break;
2959 }
2960 self.advance(); if name_lc == "default" {
2964 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2965 self.advance();
2966 }
2967 }
2968 if matches!(self.peek(), Token::Eq) {
2969 self.advance();
2970 }
2971 match self.peek() {
2972 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
2973 self.advance();
2974 }
2975 _ => {}
2976 }
2977 }
2978 }
2979
2980 fn peek_table_level_pk_start(&self) -> bool {
2985 let cur = self.peek();
2986 let nxt = self.tokens.get(self.pos + 1);
2987 let nxt2 = self.tokens.get(self.pos + 2);
2988 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
2989 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
2990 let is_lparen = matches!(nxt2, Some(Token::LParen));
2991 is_primary && is_key && is_lparen
2992 }
2993
2994 fn peek_table_level_unique_start(&self) -> bool {
2998 let cur = self.peek();
2999 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
3000 if !is_unique {
3001 return false;
3002 }
3003 let n1 = self.tokens.get(self.pos + 1);
3004 if matches!(n1, Some(Token::LParen)) {
3006 return true;
3007 }
3008 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
3010 if !is_nulls {
3011 return false;
3012 }
3013 let n2 = self.tokens.get(self.pos + 2);
3014 let n3 = self.tokens.get(self.pos + 3);
3015 let n4 = self.tokens.get(self.pos + 4);
3016 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
3018 return true;
3019 }
3020 if matches!(n2, Some(Token::Not))
3022 && matches!(n3, Some(Token::Distinct))
3023 && matches!(n4, Some(Token::LParen))
3024 {
3025 return true;
3026 }
3027 false
3028 }
3029
3030 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3031 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
3034 Ok(crate::ast::TableConstraint::PrimaryKey {
3035 name: None,
3036 columns,
3037 })
3038 }
3039
3040 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3041 self.advance(); let mut nulls_not_distinct = false;
3046 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
3047 let n1 = self.tokens.get(self.pos + 1);
3048 let n2 = self.tokens.get(self.pos + 2);
3049 let is_not = matches!(n1, Some(Token::Not));
3050 let is_distinct = matches!(n2, Some(Token::Distinct));
3051 if is_not && is_distinct {
3052 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
3056 } else if matches!(n1, Some(Token::Distinct)) {
3057 self.advance(); self.advance(); }
3060 }
3061 let columns = self.parse_paren_ident_list("UNIQUE")?;
3062 Ok(crate::ast::TableConstraint::Unique {
3063 name: None,
3064 columns,
3065 nulls_not_distinct,
3066 })
3067 }
3068
3069 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3073 self.advance(); if !matches!(self.peek(), Token::LParen) {
3075 return Err(self.err(alloc::format!(
3076 "expected '(' after CHECK, got {:?}",
3077 self.peek()
3078 )));
3079 }
3080 self.advance();
3081 let expr = self.parse_expr(0)?;
3082 if !matches!(self.peek(), Token::RParen) {
3083 return Err(self.err(alloc::format!(
3084 "expected ')' to close CHECK predicate, got {:?}",
3085 self.peek()
3086 )));
3087 }
3088 self.advance();
3089 Ok(crate::ast::TableConstraint::Check { name: None, expr })
3090 }
3091
3092 fn peek_table_level_check_start(&self) -> bool {
3094 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
3095 }
3096
3097 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
3098 if !matches!(self.peek(), Token::LParen) {
3099 return Err(self.err(alloc::format!(
3100 "expected '(' after {ctx}, got {:?}",
3101 self.peek()
3102 )));
3103 }
3104 self.advance();
3105 let mut out = Vec::new();
3106 loop {
3107 out.push(self.expect_ident_like()?);
3108 match self.peek() {
3109 Token::Comma => {
3110 self.advance();
3111 }
3112 Token::RParen => {
3113 self.advance();
3114 break;
3115 }
3116 other => {
3117 return Err(self.err(alloc::format!(
3118 "expected ',' or ')' in {ctx} list, got {other:?}"
3119 )));
3120 }
3121 }
3122 }
3123 if out.is_empty() {
3124 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
3125 }
3126 Ok(out)
3127 }
3128
3129 fn peek_constraint_or_fk_start(&self) -> bool {
3134 let is_constraint_kw = matches!(
3135 self.peek(),
3136 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3137 );
3138 let is_foreign_kw = matches!(
3139 self.peek(),
3140 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
3141 );
3142 is_constraint_kw || is_foreign_kw
3143 }
3144
3145 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
3149 let mut name: Option<String> = None;
3150 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
3151 self.advance();
3152 name = Some(self.expect_ident_like()?);
3153 }
3154 match self.advance() {
3156 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
3157 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
3158 }
3159 match self.advance() {
3161 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
3162 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
3163 }
3164 if !matches!(self.peek(), Token::LParen) {
3166 return Err(self.err(format!(
3167 "expected '(' after FOREIGN KEY, got {:?}",
3168 self.peek()
3169 )));
3170 }
3171 self.advance();
3172 let mut columns = Vec::new();
3173 loop {
3174 columns.push(self.expect_ident_like()?);
3175 match self.peek() {
3176 Token::Comma => {
3177 self.advance();
3178 }
3179 Token::RParen => {
3180 self.advance();
3181 break;
3182 }
3183 other => {
3184 return Err(self.err(format!(
3185 "expected ',' or ')' in FK column list, got {other:?}"
3186 )));
3187 }
3188 }
3189 }
3190 if columns.is_empty() {
3191 return Err(self.err("FOREIGN KEY requires at least one column".into()));
3192 }
3193 let (parent_table, parent_columns, on_delete, on_update) =
3194 self.parse_references_tail(columns.len())?;
3195 Ok(ForeignKeyConstraint {
3196 name,
3197 columns,
3198 parent_table,
3199 parent_columns,
3200 on_delete,
3201 on_update,
3202 })
3203 }
3204
3205 fn parse_references_tail(
3210 &mut self,
3211 expected_arity: usize,
3212 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
3213 match self.advance() {
3214 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
3215 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
3216 }
3217 let parent_table = self.expect_ident_like()?;
3218 let mut parent_columns: Vec<String> = Vec::new();
3219 if matches!(self.peek(), Token::LParen) {
3220 self.advance();
3221 loop {
3222 parent_columns.push(self.expect_ident_like()?);
3223 match self.peek() {
3224 Token::Comma => {
3225 self.advance();
3226 }
3227 Token::RParen => {
3228 self.advance();
3229 break;
3230 }
3231 other => {
3232 return Err(self.err(format!(
3233 "expected ',' or ')' in REFERENCES column list, got {other:?}"
3234 )));
3235 }
3236 }
3237 }
3238 }
3239 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
3240 return Err(self.err(format!(
3241 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
3242 expected_arity,
3243 parent_columns.len()
3244 )));
3245 }
3246 loop {
3252 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
3253 return Err(self.err(
3254 "DEFERRABLE constraints are not supported (SPG is single-writer; \
3255 constraints are always evaluated immediately at commit)"
3256 .into(),
3257 ));
3258 }
3259 if matches!(self.peek(), Token::Not) {
3260 let look = self.tokens.get(self.pos + 1);
3261 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
3262 self.advance();
3265 self.advance();
3266 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
3268 {
3269 self.advance();
3270 match self.advance() {
3271 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
3272 other => {
3273 return Err(self.err(format!(
3274 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
3275 got {other:?}"
3276 )));
3277 }
3278 }
3279 }
3280 continue;
3281 }
3282 break;
3283 }
3284 break;
3285 }
3286 let mut on_delete = FkAction::Restrict;
3289 let mut on_update = FkAction::Restrict;
3290 let mut seen_on_delete = false;
3291 let mut seen_on_update = false;
3292 loop {
3293 if !matches!(self.peek(), Token::On) {
3294 break;
3295 }
3296 self.advance();
3297 let which = self.advance();
3298 let action = self.parse_fk_action()?;
3299 match which {
3300 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
3301 if seen_on_delete {
3302 return Err(self.err("ON DELETE specified twice".into()));
3303 }
3304 seen_on_delete = true;
3305 on_delete = action;
3306 }
3307 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
3308 if seen_on_update {
3309 return Err(self.err("ON UPDATE specified twice".into()));
3310 }
3311 seen_on_update = true;
3312 on_update = action;
3313 }
3314 other => {
3315 return Err(
3316 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
3317 );
3318 }
3319 }
3320 }
3321 Ok((parent_table, parent_columns, on_delete, on_update))
3322 }
3323
3324 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
3327 match self.advance() {
3328 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
3329 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
3330 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
3331 Token::Null => Ok(FkAction::SetNull),
3332 Token::Default => Ok(FkAction::SetDefault),
3333 other => Err(self.err(format!(
3334 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
3335 ))),
3336 },
3337 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
3338 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
3339 other => Err(self.err(format!(
3340 "expected ACTION after NO in FK action, got {other:?}"
3341 ))),
3342 },
3343 other => Err(self.err(format!(
3344 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
3345 ))),
3346 }
3347 }
3348
3349 fn consume_if_not_exists(&mut self) -> bool {
3352 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3356 if !looks_like_if {
3357 return false;
3358 }
3359 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
3362 return false;
3363 }
3364 if !matches!(
3365 self.tokens.get(self.pos + 2),
3366 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3367 ) {
3368 return false;
3369 }
3370 self.advance(); self.advance(); self.advance(); true
3374 }
3375
3376 fn consume_if_exists(&mut self) -> bool {
3380 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3381 if !looks_like_if {
3382 return false;
3383 }
3384 if !matches!(
3385 self.tokens.get(self.pos + 1),
3386 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3387 ) {
3388 return false;
3389 }
3390 self.advance(); self.advance(); true
3393 }
3394
3395 fn consume_optional_index_column_qualifiers(&mut self) {
3401 loop {
3402 match self.peek() {
3403 Token::Asc | Token::Desc => {
3404 self.advance();
3405 }
3406 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
3407 let look = self.tokens.get(self.pos + 1);
3408 if matches!(
3409 look,
3410 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
3411 || k.eq_ignore_ascii_case("last")
3412 ) {
3413 self.advance();
3414 self.advance();
3415 } else {
3416 break;
3417 }
3418 }
3419 _ => break,
3420 }
3421 }
3422 }
3423
3424 fn parse_create_index_stmt_after_create(
3425 &mut self,
3426 is_unique: bool,
3427 ) -> Result<Statement, ParseError> {
3428 debug_assert!(matches!(self.peek(), Token::Index));
3430 self.advance();
3431 let if_not_exists = self.consume_if_not_exists();
3432 let name = self.expect_ident_like()?;
3433 if !matches!(self.peek(), Token::On) {
3434 return Err(self.err(format!(
3435 "expected ON after CREATE INDEX <name>, got {:?}",
3436 self.peek()
3437 )));
3438 }
3439 self.advance();
3440 let table = self.expect_ident_like()?;
3441 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3446 self.advance();
3447 let m = self.expect_ident_like()?;
3448 match m.to_ascii_lowercase().as_str() {
3449 "hnsw" => IndexMethod::Hnsw,
3450 "btree" => IndexMethod::BTree,
3451 "brin" => IndexMethod::Brin,
3452 "gin" => IndexMethod::Gin,
3457 "gist" | "spgist" | "hash" => IndexMethod::BTree,
3466 "ivfflat" => IndexMethod::Hnsw,
3474 other => {
3475 return Err(self.err(alloc::format!(
3476 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
3477 )));
3478 }
3479 }
3480 } else {
3481 IndexMethod::BTree
3482 };
3483 if !matches!(self.peek(), Token::LParen) {
3484 return Err(self.err(format!(
3485 "expected '(' before indexed column, got {:?}",
3486 self.peek()
3487 )));
3488 }
3489 self.advance();
3490 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
3499 Token::Ident(s) | Token::QuotedIdent(s)
3506 if matches!(
3507 self.tokens.get(self.pos + 1),
3508 Some(Token::RParen | Token::Comma)
3509 ) =>
3510 {
3511 self.advance();
3512 (s, None)
3513 }
3514 Token::Ident(s) | Token::QuotedIdent(s)
3522 if matches!(
3523 self.tokens.get(self.pos + 1),
3524 Some(Token::Ident(op) | Token::QuotedIdent(op))
3525 if is_vector_opclass_name(op)
3526 ) =>
3527 {
3528 self.advance(); self.advance(); (s, None)
3531 }
3532 Token::Ident(_) | Token::QuotedIdent(_) => {
3533 let key_expr = self.parse_expr(0)?;
3534 let primary = extract_first_column(&key_expr).ok_or_else(|| {
3535 self.err("expression index key must reference at least one column".into())
3536 })?;
3537 (primary, Some(key_expr))
3538 }
3539 other => {
3540 return Err(self.err(format!(
3541 "expected column ident or expression, got {other:?}"
3542 )));
3543 }
3544 };
3545 let mut extra_columns: Vec<String> = Vec::new();
3554 self.consume_optional_index_column_qualifiers();
3556 while matches!(self.peek(), Token::Comma) {
3557 self.advance();
3558 let extra = self.expect_ident_like()?;
3559 self.consume_optional_index_column_qualifiers();
3560 extra_columns.push(extra);
3561 }
3562 if !matches!(self.peek(), Token::RParen) {
3563 return Err(self.err(format!(
3564 "expected ')' after indexed column / expression, got {:?}",
3565 self.peek()
3566 )));
3567 }
3568 self.advance();
3569 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
3573 {
3574 self.advance();
3575 if !matches!(self.peek(), Token::LParen) {
3576 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
3577 }
3578 self.advance();
3579 let mut cols = Vec::new();
3580 loop {
3581 cols.push(self.expect_ident_like()?);
3582 match self.peek() {
3583 Token::Comma => {
3584 self.advance();
3585 }
3586 Token::RParen => {
3587 self.advance();
3588 break;
3589 }
3590 other => {
3591 return Err(self.err(format!(
3592 "expected ',' or ')' in INCLUDE list, got {other:?}"
3593 )));
3594 }
3595 }
3596 }
3597 cols
3598 } else {
3599 Vec::new()
3600 };
3601 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3607 self.advance();
3608 if !matches!(self.peek(), Token::LParen) {
3609 return Err(self.err(format!(
3610 "expected '(' after WITH in CREATE INDEX, got {:?}",
3611 self.peek()
3612 )));
3613 }
3614 self.advance();
3615 loop {
3616 if matches!(self.peek(), Token::RParen) {
3617 self.advance();
3618 break;
3619 }
3620 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
3623 self.advance();
3624 let _ = self.advance(); }
3626 match self.peek() {
3627 Token::Comma => {
3628 self.advance();
3629 }
3630 Token::RParen => {
3631 self.advance();
3632 break;
3633 }
3634 other => {
3635 return Err(self.err(format!(
3636 "expected ',' or ')' in WITH (…) clause, got {other:?}"
3637 )));
3638 }
3639 }
3640 }
3641 }
3642 let partial_predicate = if matches!(self.peek(), Token::Where) {
3644 self.advance();
3645 Some(self.parse_expr(0)?)
3646 } else {
3647 None
3648 };
3649 if is_unique && !matches!(method, IndexMethod::BTree) {
3654 return Err(self.err(alloc::format!(
3655 "UNIQUE is only supported on BTree indexes, got USING {:?}",
3656 method
3657 )));
3658 }
3659 Ok(Statement::CreateIndex(CreateIndexStatement {
3660 name,
3661 table,
3662 column,
3663 method,
3664 if_not_exists,
3665 included_columns,
3666 partial_predicate,
3667 extra_columns: extra_columns.clone(),
3668 expression,
3669 is_unique,
3670 }))
3671 }
3672
3673 fn parse_column_def_with_fk(
3678 &mut self,
3679 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
3680 let col = self.parse_column_def()?;
3681 let inline_references = matches!(
3683 self.peek(),
3684 Token::Ident(s) if s.eq_ignore_ascii_case("references")
3685 );
3686 if !inline_references {
3687 return Ok((col, None));
3688 }
3689 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
3690 let fk = ForeignKeyConstraint {
3691 name: None,
3692 columns: vec![col.name.clone()],
3693 parent_table,
3694 parent_columns,
3695 on_delete,
3696 on_update,
3697 };
3698 Ok((col, Some(fk)))
3699 }
3700
3701 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
3709 let (ty, _, _) = self.parse_type_with_implied_flags()?;
3710 Ok(ty)
3711 }
3712
3713 fn parse_type_with_implied_flags(
3714 &mut self,
3715 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
3716 let ty_ident = match self.advance() {
3717 Token::Ident(s) => s,
3718 other => {
3719 return Err(ParseError {
3720 message: format!("expected column type, got {other:?}"),
3721 token_pos: self.pos.saturating_sub(1),
3722 });
3723 }
3724 };
3725 let mut implied_auto_increment = false;
3726 let mut implied_not_null = false;
3727 let mut ty = match ty_ident.as_str() {
3728 "smallserial" | "serial2" => {
3730 implied_auto_increment = true;
3731 implied_not_null = true;
3732 ColumnTypeName::SmallInt
3733 }
3734 "serial" | "serial4" => {
3735 implied_auto_increment = true;
3736 implied_not_null = true;
3737 ColumnTypeName::Int
3738 }
3739 "bigserial" | "serial8" => {
3740 implied_auto_increment = true;
3741 implied_not_null = true;
3742 ColumnTypeName::BigInt
3743 }
3744 "smallint" | "tinyint" => {
3750 self.consume_optional_paren_size();
3755 ColumnTypeName::SmallInt
3756 }
3757 "int" | "integer" | "mediumint" => {
3758 self.consume_optional_paren_size();
3759 ColumnTypeName::Int
3760 }
3761 "bigint" => {
3762 self.consume_optional_paren_size();
3763 ColumnTypeName::BigInt
3764 }
3765 "float" | "double" | "real" => {
3770 if ty_ident.eq_ignore_ascii_case("double")
3771 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
3772 {
3773 self.advance();
3774 }
3775 ColumnTypeName::Float
3776 }
3777 "float4" | "float8" => ColumnTypeName::Float,
3779 "text" => ColumnTypeName::Text,
3780 "bool" | "boolean" => ColumnTypeName::Bool,
3781 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
3782 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
3783 "vector" => {
3784 let dim = self.parse_paren_size("VECTOR")?;
3785 let encoding = self.parse_optional_vector_encoding()?;
3786 ColumnTypeName::Vector { dim, encoding }
3787 }
3788 "numeric" => {
3789 let (precision, scale) = self.parse_optional_numeric_params()?;
3790 ColumnTypeName::Numeric(precision, scale)
3791 }
3792 "date" => ColumnTypeName::Date,
3793 "timestamp" | "datetime" => {
3796 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
3802 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
3803 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
3804 {
3805 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
3809 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
3810 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
3811 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
3812 {
3813 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
3817 } else {
3818 self.consume_optional_paren_size();
3822 ColumnTypeName::Timestamp
3823 }
3824 }
3825 "timestamptz" => ColumnTypeName::Timestamptz,
3829 "json" => ColumnTypeName::Json,
3834 "jsonb" => ColumnTypeName::Jsonb,
3835 "bytea" | "bytes" => ColumnTypeName::Bytes,
3841 "tsvector" => ColumnTypeName::TsVector,
3846 "tsquery" => ColumnTypeName::TsQuery,
3847 other => {
3848 return Err(ParseError {
3849 message: format!("unsupported column type {other:?}"),
3850 token_pos: self.pos.saturating_sub(1),
3851 });
3852 }
3853 };
3854 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
3859 self.advance();
3860 }
3861 loop {
3867 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
3868 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
3869 {
3870 self.advance(); self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_))
3873 {
3874 self.advance();
3875 }
3876 continue;
3877 }
3878 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
3879 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_))
3881 {
3882 self.advance();
3883 }
3884 continue;
3885 }
3886 break;
3887 }
3888 if matches!(self.peek(), Token::LBracket) {
3893 self.advance();
3894 if !matches!(self.peek(), Token::RBracket) {
3895 return Err(self.err(alloc::format!(
3896 "TEXT[] takes no dimension; got {:?}",
3897 self.peek()
3898 )));
3899 }
3900 self.advance();
3901 ty = match ty {
3905 ColumnTypeName::Text => ColumnTypeName::TextArray,
3906 ColumnTypeName::Int => ColumnTypeName::IntArray,
3907 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
3908 other => {
3909 return Err(self.err(alloc::format!(
3910 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
3911 )));
3912 }
3913 };
3914 }
3915 Ok((ty, implied_auto_increment, implied_not_null))
3916 }
3917
3918 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
3919 let name = self.expect_ident_like()?;
3920 let (ty, implied_auto_increment, implied_not_null) =
3921 self.parse_type_with_implied_flags()?;
3922 let mut default: Option<Expr> = None;
3926 let mut nullable = !implied_not_null;
3927 let mut nullability_seen = implied_not_null;
3928 let mut auto_increment = implied_auto_increment;
3929 let mut is_primary_key = false;
3930 let mut is_unique = false;
3931 let mut check: Option<Expr> = None;
3932 loop {
3933 if matches!(self.peek(), Token::Default) {
3934 if default.is_some() {
3935 return Err(self.err("DEFAULT specified twice".into()));
3936 }
3937 self.advance();
3938 default = Some(self.parse_expr(0)?);
3939 continue;
3940 }
3941 if matches!(self.peek(), Token::Not) {
3942 if nullability_seen {
3943 return Err(self.err("NOT NULL specified twice".into()));
3944 }
3945 self.advance();
3946 if !matches!(self.peek(), Token::Null) {
3947 return Err(self.err(format!(
3948 "expected NULL after NOT in column def, got {:?}",
3949 self.peek()
3950 )));
3951 }
3952 self.advance();
3953 nullable = false;
3954 nullability_seen = true;
3955 continue;
3956 }
3957 if matches!(self.peek(), Token::Null) {
3963 if nullability_seen && !nullable {
3964 return Err(self.err(
3965 "column declared NOT NULL then NULL — pick one".into(),
3966 ));
3967 }
3968 self.advance();
3969 nullable = true;
3970 nullability_seen = true;
3971 continue;
3972 }
3973 if let Token::Ident(s) = self.peek()
3976 && (s.eq_ignore_ascii_case("auto_increment")
3977 || s.eq_ignore_ascii_case("autoincrement"))
3978 {
3979 if auto_increment {
3980 return Err(self.err("AUTO_INCREMENT specified twice".into()));
3981 }
3982 self.advance();
3983 auto_increment = true;
3984 continue;
3985 }
3986 if let Token::Ident(s) = self.peek()
3991 && s.eq_ignore_ascii_case("primary")
3992 {
3993 if is_primary_key {
3994 return Err(self.err("PRIMARY KEY specified twice".into()));
3995 }
3996 let next = self.tokens.get(self.pos + 1);
3998 let next_is_key = matches!(
3999 next,
4000 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
4001 );
4002 if !next_is_key {
4003 return Err(self.err(format!(
4004 "expected KEY after PRIMARY in column def, got {:?}",
4005 next
4006 )));
4007 }
4008 self.advance(); self.advance(); is_primary_key = true;
4011 if nullability_seen && nullable {
4012 return Err(self.err(
4013 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
4014 ));
4015 }
4016 nullable = false;
4017 nullability_seen = true;
4018 continue;
4019 }
4020 if let Token::Ident(s) = self.peek()
4024 && s.eq_ignore_ascii_case("unique")
4025 {
4026 if is_unique {
4027 return Err(self.err("UNIQUE specified twice".into()));
4028 }
4029 self.advance();
4030 is_unique = true;
4031 continue;
4032 }
4033 if let Token::Ident(s) = self.peek()
4038 && s.eq_ignore_ascii_case("check")
4039 {
4040 self.advance();
4041 if !matches!(self.peek(), Token::LParen) {
4042 return Err(self.err(alloc::format!(
4043 "expected '(' after CHECK in column def, got {:?}",
4044 self.peek()
4045 )));
4046 }
4047 self.advance();
4048 let pred = self.parse_expr(0)?;
4049 if !matches!(self.peek(), Token::RParen) {
4050 return Err(self.err(alloc::format!(
4051 "expected ')' to close CHECK predicate, got {:?}",
4052 self.peek()
4053 )));
4054 }
4055 self.advance();
4056 check = Some(match check.take() {
4057 Some(prev) => Expr::Binary {
4058 op: BinOp::And,
4059 lhs: Box::new(prev),
4060 rhs: Box::new(pred),
4061 },
4062 None => pred,
4063 });
4064 continue;
4065 }
4066 break;
4067 }
4068 Ok(ColumnDef {
4069 name,
4070 ty,
4071 nullable,
4072 default,
4073 auto_increment,
4074 is_primary_key,
4075 is_unique,
4076 check,
4077 })
4078 }
4079
4080 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
4084 if !matches!(self.peek(), Token::LParen) {
4085 return Ok((0, 0));
4089 }
4090 self.advance();
4091 let precision = match self.advance() {
4092 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
4093 other => {
4094 return Err(ParseError {
4095 message: format!(
4096 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
4097 ),
4098 token_pos: self.pos.saturating_sub(1),
4099 });
4100 }
4101 };
4102 let scale = if matches!(self.peek(), Token::Comma) {
4103 self.advance();
4104 match self.advance() {
4105 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
4106 u8::try_from(n).expect("range-checked")
4107 }
4108 other => {
4109 return Err(ParseError {
4110 message: format!(
4111 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
4112 ),
4113 token_pos: self.pos.saturating_sub(1),
4114 });
4115 }
4116 }
4117 } else {
4118 0
4119 };
4120 if !matches!(self.peek(), Token::RParen) {
4121 return Err(self.err(format!(
4122 "expected ')' to close NUMERIC params, got {:?}",
4123 self.peek()
4124 )));
4125 }
4126 self.advance();
4127 Ok((precision, scale))
4128 }
4129
4130 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
4138 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4139 return Ok(VecEncoding::F32);
4140 }
4141 let n1 = self.tokens.get(self.pos + 1);
4147 let next_is_encoding = matches!(
4148 n1,
4149 Some(Token::Ident(s))
4150 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
4151 );
4152 if !next_is_encoding {
4153 return Ok(VecEncoding::F32);
4154 }
4155 self.advance();
4156 let enc_ident = match self.advance() {
4157 Token::Ident(s) => s,
4158 other => {
4159 return Err(self.err(format!(
4160 "expected vector encoding after USING, got {other:?}"
4161 )));
4162 }
4163 };
4164 match enc_ident.to_ascii_lowercase().as_str() {
4165 "sq8" => Ok(VecEncoding::Sq8),
4166 "half" => Ok(VecEncoding::F16),
4169 other => Err(self.err(format!(
4170 "unknown vector encoding {other:?}; supported: SQ8, HALF"
4171 ))),
4172 }
4173 }
4174
4175 fn consume_optional_paren_size(&mut self) {
4179 if !matches!(self.peek(), Token::LParen) {
4180 return;
4181 }
4182 self.advance();
4183 let mut depth = 1usize;
4185 while depth > 0 {
4186 match self.peek() {
4187 Token::LParen => depth += 1,
4188 Token::RParen => depth -= 1,
4189 Token::Eof => return,
4190 _ => {}
4191 }
4192 self.advance();
4193 }
4194 }
4195
4196 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
4197 if !matches!(self.peek(), Token::LParen) {
4198 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
4199 }
4200 self.advance();
4201 let n = match self.advance() {
4202 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
4203 message: format!("{label} size too large: {n}"),
4204 token_pos: self.pos.saturating_sub(1),
4205 })?,
4206 other => {
4207 return Err(ParseError {
4208 message: format!("expected positive integer {label} size, got {other:?}"),
4209 token_pos: self.pos.saturating_sub(1),
4210 });
4211 }
4212 };
4213 if !matches!(self.peek(), Token::RParen) {
4214 return Err(self.err(format!(
4215 "expected ')' after {label} size, got {:?}",
4216 self.peek()
4217 )));
4218 }
4219 self.advance();
4220 Ok(n)
4221 }
4222
4223 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
4224 debug_assert!(matches!(self.peek(), Token::Insert));
4225 self.advance();
4226 if !matches!(self.peek(), Token::Into) {
4227 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
4228 }
4229 self.advance();
4230 let table = self.expect_ident_like()?;
4231 let columns = if matches!(self.peek(), Token::LParen) {
4233 self.advance();
4234 let mut names = Vec::new();
4235 loop {
4236 names.push(self.expect_ident_like()?);
4237 match self.peek() {
4238 Token::Comma => {
4239 self.advance();
4240 }
4241 Token::RParen => {
4242 self.advance();
4243 break;
4244 }
4245 other => {
4246 return Err(self.err(format!(
4247 "expected ',' or ')' in INSERT column list, got {other:?}"
4248 )));
4249 }
4250 }
4251 }
4252 Some(names)
4253 } else {
4254 None
4255 };
4256 if matches!(self.peek(), Token::Select) {
4259 let select_stmt = match self.parse_select_stmt()? {
4260 Statement::Select(s) => s,
4261 other => {
4262 return Err(self.err(alloc::format!(
4263 "expected SELECT after INSERT INTO ... target, got {other:?}"
4264 )));
4265 }
4266 };
4267 let on_conflict = self.parse_optional_on_conflict()?;
4268 let returning = self.parse_optional_returning()?;
4269 return Ok(Statement::Insert(InsertStatement {
4270 table,
4271 columns,
4272 rows: Vec::new(),
4273 select_source: Some(Box::new(select_stmt)),
4274 on_conflict,
4275 returning,
4276 }));
4277 }
4278 if !matches!(self.peek(), Token::Values) {
4279 return Err(self.err(format!(
4280 "expected VALUES or SELECT after table name, got {:?}",
4281 self.peek()
4282 )));
4283 }
4284 self.advance();
4285 if !matches!(self.peek(), Token::LParen) {
4286 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
4287 }
4288 let mut rows = Vec::new();
4289 loop {
4290 if !matches!(self.peek(), Token::LParen) {
4292 return Err(self.err(format!(
4293 "expected '(' for next VALUES tuple, got {:?}",
4294 self.peek()
4295 )));
4296 }
4297 self.advance();
4298 let mut tuple = Vec::new();
4299 loop {
4300 tuple.push(self.parse_expr(0)?);
4301 match self.peek() {
4302 Token::Comma => {
4303 self.advance();
4304 }
4305 Token::RParen => {
4306 self.advance();
4307 break;
4308 }
4309 other => {
4310 return Err(self.err(format!(
4311 "expected ',' or ')' in VALUES tuple, got {other:?}"
4312 )));
4313 }
4314 }
4315 }
4316 if tuple.is_empty() {
4317 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
4318 }
4319 rows.push(tuple);
4320 if matches!(self.peek(), Token::Comma) {
4322 self.advance();
4323 } else {
4324 break;
4325 }
4326 }
4327 let on_conflict = self.parse_optional_on_conflict()?;
4328 let returning = self.parse_optional_returning()?;
4329 Ok(Statement::Insert(InsertStatement {
4330 table,
4331 columns,
4332 rows,
4333 select_source: None,
4334 on_conflict,
4335 returning,
4336 }))
4337 }
4338
4339 fn parse_optional_on_conflict(
4344 &mut self,
4345 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
4346 if !matches!(self.peek(), Token::On) {
4347 return Ok(None);
4348 }
4349 let next_is_conflict = matches!(
4352 self.tokens.get(self.pos + 1),
4353 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
4354 );
4355 if !next_is_conflict {
4356 return Ok(None);
4357 }
4358 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
4362 if matches!(self.peek(), Token::LParen) {
4363 self.advance();
4364 loop {
4365 target_columns.push(self.expect_ident_like()?);
4366 match self.peek() {
4367 Token::Comma => {
4368 self.advance();
4369 }
4370 Token::RParen => {
4371 self.advance();
4372 break;
4373 }
4374 other => {
4375 return Err(self.err(alloc::format!(
4376 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
4377 )));
4378 }
4379 }
4380 }
4381 }
4382 match self.advance() {
4384 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
4385 other => {
4386 return Err(self.err(alloc::format!(
4387 "expected DO after ON CONFLICT [(…)], got {other:?}"
4388 )));
4389 }
4390 }
4391 let action = match self.advance() {
4393 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
4394 crate::ast::OnConflictAction::Nothing
4395 }
4396 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
4397 self.parse_on_conflict_update_action()?
4398 }
4399 other => {
4400 return Err(self.err(alloc::format!(
4401 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
4402 )));
4403 }
4404 };
4405 Ok(Some(crate::ast::OnConflictClause {
4406 target_columns,
4407 action,
4408 }))
4409 }
4410
4411 fn parse_on_conflict_update_action(
4415 &mut self,
4416 ) -> Result<crate::ast::OnConflictAction, ParseError> {
4417 match self.advance() {
4419 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
4420 other => {
4421 return Err(self.err(alloc::format!(
4422 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
4423 )));
4424 }
4425 }
4426 let mut assignments: Vec<(String, Expr)> = Vec::new();
4427 loop {
4428 let col = self.expect_ident_like()?;
4429 if !matches!(self.peek(), Token::Eq) {
4430 return Err(self.err(alloc::format!(
4431 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
4432 self.peek()
4433 )));
4434 }
4435 self.advance();
4436 let value = self.parse_expr(0)?;
4437 assignments.push((col, value));
4438 if matches!(self.peek(), Token::Comma) {
4439 self.advance();
4440 continue;
4441 }
4442 break;
4443 }
4444 let where_ = if matches!(self.peek(), Token::Where) {
4445 self.advance();
4446 Some(self.parse_expr(0)?)
4447 } else {
4448 None
4449 };
4450 Ok(crate::ast::OnConflictAction::Update {
4451 assignments,
4452 where_,
4453 })
4454 }
4455
4456 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
4457 let mut items = Vec::new();
4458 loop {
4459 items.push(self.parse_select_item()?);
4460 if matches!(self.peek(), Token::Comma) {
4461 self.advance();
4462 } else {
4463 break;
4464 }
4465 }
4466 Ok(items)
4467 }
4468
4469 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
4470 if matches!(self.peek(), Token::Star) {
4471 self.advance();
4472 return Ok(SelectItem::Wildcard);
4473 }
4474 let expr = self.parse_expr(0)?;
4475 let alias = self.parse_optional_alias();
4476 Ok(SelectItem::Expr { expr, alias })
4477 }
4478
4479 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
4480 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
4484 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4485 {
4486 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
4489 if !matches!(self.peek(), Token::RParen) {
4490 return Err(self.err(alloc::format!(
4491 "expected ')' after unnest() argument, got {:?}",
4492 self.peek()
4493 )));
4494 }
4495 self.advance();
4496 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
4497 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
4498 return Ok(TableRef {
4499 name,
4500 alias: alias_ident,
4501 as_of_segment: None,
4502 unnest_expr: Some(Box::new(expr)),
4503 unnest_column_aliases,
4504 });
4505 }
4506 let name = self.expect_ident_like()?;
4507 let as_of_segment = if matches!(self.peek(), Token::As)
4513 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
4514 {
4515 self.advance(); self.advance(); let kw = match self.peek().clone() {
4518 Token::Ident(s) | Token::QuotedIdent(s) => s,
4519 other => {
4520 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
4521 }
4522 };
4523 if !kw.eq_ignore_ascii_case("segment") {
4524 return Err(self.err(format!(
4525 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
4526 )));
4527 }
4528 self.advance();
4529 let id = match self.advance() {
4532 Token::String(s) => s
4533 .parse::<u32>()
4534 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4535 Token::Integer(n) => u32::try_from(n)
4536 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4537 other => {
4538 return Err(self.err(format!(
4539 "expected segment id literal after AS OF SEGMENT, got {other:?}"
4540 )));
4541 }
4542 };
4543 Some(id)
4544 } else {
4545 None
4546 };
4547 let alias = self.parse_optional_alias();
4548 Ok(TableRef {
4549 name,
4550 alias,
4551 as_of_segment,
4552 unnest_expr: None,
4553 unnest_column_aliases: Vec::new(),
4554 })
4555 }
4556
4557 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
4563 let alias = self.parse_optional_alias();
4564 if alias.is_none() {
4565 return (None, Vec::new());
4566 }
4567 let mut cols: Vec<String> = Vec::new();
4568 if matches!(self.peek(), Token::LParen) {
4569 self.advance();
4570 loop {
4571 match self.peek().clone() {
4572 Token::Ident(s) | Token::QuotedIdent(s) => {
4573 self.advance();
4574 cols.push(s);
4575 }
4576 _ => break,
4577 }
4578 if matches!(self.peek(), Token::Comma) {
4579 self.advance();
4580 continue;
4581 }
4582 break;
4583 }
4584 if matches!(self.peek(), Token::RParen) {
4585 self.advance();
4586 }
4587 }
4588 (alias, cols)
4589 }
4590
4591 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
4596 let primary = self.parse_table_ref()?;
4597 let mut joins = Vec::new();
4598 loop {
4599 if matches!(self.peek(), Token::Comma) {
4601 self.advance();
4602 let table = self.parse_table_ref()?;
4603 joins.push(FromJoin {
4604 kind: JoinKind::Cross,
4605 table,
4606 on: None,
4607 });
4608 continue;
4609 }
4610 let kind =
4613 match self.peek() {
4614 Token::Inner => {
4615 self.advance();
4616 if !matches!(self.peek(), Token::Join) {
4617 return Err(self
4618 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
4619 }
4620 self.advance();
4621 JoinKind::Inner
4622 }
4623 Token::Left => {
4624 self.advance();
4625 if matches!(self.peek(), Token::Outer) {
4626 self.advance();
4627 }
4628 if !matches!(self.peek(), Token::Join) {
4629 return Err(self.err(format!(
4630 "expected JOIN after LEFT [OUTER], got {:?}",
4631 self.peek()
4632 )));
4633 }
4634 self.advance();
4635 JoinKind::Left
4636 }
4637 Token::Cross => {
4638 self.advance();
4639 if !matches!(self.peek(), Token::Join) {
4640 return Err(self
4641 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
4642 }
4643 self.advance();
4644 JoinKind::Cross
4645 }
4646 Token::Join => {
4647 self.advance();
4648 JoinKind::Inner
4649 }
4650 _ => break,
4651 };
4652 let table = self.parse_table_ref()?;
4653 let on = if matches!(self.peek(), Token::On) {
4654 self.advance();
4655 Some(self.parse_expr(0)?)
4656 } else if kind == JoinKind::Cross {
4657 None
4658 } else {
4659 return Err(self.err(format!(
4660 "expected ON after {:?} JOIN, got {:?}",
4661 kind,
4662 self.peek()
4663 )));
4664 };
4665 joins.push(FromJoin { kind, table, on });
4666 }
4667 Ok(FromClause { primary, joins })
4668 }
4669
4670 fn parse_optional_alias(&mut self) -> Option<String> {
4675 if matches!(self.peek(), Token::As) {
4676 self.advance();
4677 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
4682 return self.expect_ident_like().ok();
4683 }
4684 return None;
4685 }
4686 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
4687 return self.expect_ident_like().ok();
4688 }
4689 None
4690 }
4691
4692 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
4694 let mut lhs = self.parse_unary()?;
4695 while let Some((op, prec)) = binop_from(self.peek()) {
4696 if prec < min_prec {
4697 break;
4698 }
4699 self.advance();
4700 let any_kind = match self.peek() {
4705 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
4706 Some(false)
4707 }
4708 Token::Ident(s) | Token::QuotedIdent(s)
4709 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
4710 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
4711 {
4712 Some(s.eq_ignore_ascii_case("any"))
4713 }
4714 _ => None,
4715 };
4716 if let Some(is_any) = any_kind {
4717 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
4720 if !matches!(self.peek(), Token::RParen) {
4721 return Err(self.err(alloc::format!(
4722 "expected ')' after ANY/ALL argument, got {:?}",
4723 self.peek()
4724 )));
4725 }
4726 self.advance();
4727 lhs = Expr::AnyAll {
4728 expr: Box::new(lhs),
4729 op,
4730 array: Box::new(arr),
4731 is_any,
4732 };
4733 continue;
4734 }
4735 let rhs = self.parse_expr(prec + 1)?;
4736 lhs = Expr::Binary {
4737 lhs: Box::new(lhs),
4738 op,
4739 rhs: Box::new(rhs),
4740 };
4741 }
4742 Ok(lhs)
4743 }
4744
4745 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
4746 match self.peek() {
4747 Token::Not => {
4748 self.advance();
4749 let e = self.parse_expr(3)?;
4752 Ok(Expr::Unary {
4753 op: UnOp::Not,
4754 expr: Box::new(e),
4755 })
4756 }
4757 Token::Minus => {
4758 self.advance();
4759 let e = self.parse_expr(8)?;
4762 Ok(Expr::Unary {
4763 op: UnOp::Neg,
4764 expr: Box::new(e),
4765 })
4766 }
4767 _ => self.parse_atom(),
4768 }
4769 }
4770
4771 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
4772 let tok_pos = self.pos;
4773 match self.advance() {
4774 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
4775 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
4776 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
4777 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
4778 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
4779 Token::Null => Ok(Expr::Literal(Literal::Null)),
4780 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
4784 Token::LParen => {
4785 if matches!(self.peek(), Token::Select) {
4789 let inner = self.parse_select_stmt()?;
4790 match self.advance() {
4791 Token::RParen => {
4792 let Statement::Select(s) = inner else {
4793 unreachable!("parse_select_stmt returns Select")
4794 };
4795 Ok(Expr::ScalarSubquery(Box::new(s)))
4796 }
4797 other => Err(ParseError {
4798 message: format!("expected ')' after scalar subquery, got {other:?}"),
4799 token_pos: self.pos.saturating_sub(1),
4800 }),
4801 }
4802 } else {
4803 let e = self.parse_expr(0)?;
4804 match self.advance() {
4805 Token::RParen => Ok(e),
4806 other => Err(ParseError {
4807 message: format!("expected ')', got {other:?}"),
4808 token_pos: self.pos.saturating_sub(1),
4809 }),
4810 }
4811 }
4812 }
4813 Token::LBracket => self.parse_vector_literal_body(),
4814 Token::Extract => self.parse_extract_atom(),
4815 Token::Interval => self.parse_interval_atom(),
4816 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
4821 self.parse_exists_atom(false)
4822 }
4823 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
4827 self.parse_case_atom()
4828 }
4829 Token::Ident(s) | Token::QuotedIdent(s)
4833 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
4834 {
4835 self.advance(); let mut items: Vec<Expr> = Vec::new();
4837 if !matches!(self.peek(), Token::RBracket) {
4838 loop {
4839 items.push(self.parse_expr(0)?);
4840 match self.peek() {
4841 Token::Comma => {
4842 self.advance();
4843 }
4844 Token::RBracket => break,
4845 other => {
4846 return Err(self.err(alloc::format!(
4847 "expected ',' or ']' in ARRAY literal, got {other:?}"
4848 )));
4849 }
4850 }
4851 }
4852 }
4853 self.advance(); Ok(Expr::Array(items))
4855 }
4856 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
4857 other => Err(ParseError {
4858 message: format!("unexpected token {other:?} in expression"),
4859 token_pos: tok_pos,
4860 }),
4861 }
4862 .and_then(|atom| self.finish_postfix_casts(atom))
4864 }
4865
4866 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
4869 loop {
4870 if matches!(self.peek(), Token::DoubleColon) {
4871 self.advance();
4872 let target = match self.advance() {
4877 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
4878 "int" | "integer" | "int4" => {
4879 if matches!(self.peek(), Token::LBracket)
4880 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4881 {
4882 self.advance();
4883 self.advance();
4884 CastTarget::IntArray
4885 } else {
4886 CastTarget::Int
4887 }
4888 }
4889 "bigint" | "int8" => {
4890 if matches!(self.peek(), Token::LBracket)
4891 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4892 {
4893 self.advance();
4894 self.advance();
4895 CastTarget::BigIntArray
4896 } else {
4897 CastTarget::BigInt
4898 }
4899 }
4900 "float" | "double" | "real" => CastTarget::Float,
4901 "text" => {
4902 if matches!(self.peek(), Token::LBracket)
4904 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4905 {
4906 self.advance();
4907 self.advance();
4908 CastTarget::TextArray
4909 } else {
4910 CastTarget::Text
4911 }
4912 }
4913 "bool" | "boolean" => CastTarget::Bool,
4914 "vector" => CastTarget::Vector,
4915 "date" => CastTarget::Date,
4916 "timestamp" | "datetime" => CastTarget::Timestamp,
4917 "timestamptz" => CastTarget::Timestamptz,
4918 "interval" => CastTarget::Interval,
4919 "json" => CastTarget::Json,
4920 "jsonb" => CastTarget::Jsonb,
4921 "regtype" => CastTarget::RegType,
4922 "regclass" => CastTarget::RegClass,
4923 "tsvector" => CastTarget::TsVector,
4927 "tsquery" => CastTarget::TsQuery,
4928 other => {
4929 return Err(ParseError {
4930 message: format!("unsupported cast target `::{other}`"),
4931 token_pos: self.pos.saturating_sub(1),
4932 });
4933 }
4934 },
4935 Token::Interval => CastTarget::Interval,
4936 other => {
4937 return Err(ParseError {
4938 message: format!("expected type ident after `::`, got {other:?}"),
4939 token_pos: self.pos.saturating_sub(1),
4940 });
4941 }
4942 };
4943 expr = Expr::Cast {
4944 expr: Box::new(expr),
4945 target,
4946 };
4947 continue;
4948 }
4949 if matches!(self.peek(), Token::Is) {
4950 self.advance();
4951 let negated = if matches!(self.peek(), Token::Not) {
4952 self.advance();
4953 true
4954 } else {
4955 false
4956 };
4957 if matches!(self.peek(), Token::Distinct) {
4960 self.advance();
4961 if !matches!(self.peek(), Token::From) {
4962 return Err(self.err(format!(
4963 "expected FROM after IS{} DISTINCT, got {:?}",
4964 if negated { " NOT" } else { "" },
4965 self.peek()
4966 )));
4967 }
4968 self.advance();
4969 let rhs = self.parse_expr(20)?;
4973 let op = if negated {
4974 BinOp::IsNotDistinctFrom
4975 } else {
4976 BinOp::IsDistinctFrom
4977 };
4978 expr = Expr::Binary {
4979 op,
4980 lhs: Box::new(expr),
4981 rhs: Box::new(rhs),
4982 };
4983 continue;
4984 }
4985 if !matches!(self.peek(), Token::Null) {
4986 return Err(self.err(format!(
4987 "expected NULL or DISTINCT after IS{}, got {:?}",
4988 if negated { " NOT" } else { "" },
4989 self.peek()
4990 )));
4991 }
4992 self.advance();
4993 expr = Expr::IsNull {
4994 expr: Box::new(expr),
4995 negated,
4996 };
4997 continue;
4998 }
4999 let negated = if matches!(self.peek(), Token::Not) {
5003 let next = self.tokens.get(self.pos + 1);
5004 matches!(next, Some(Token::Between | Token::In | Token::Like))
5005 } else {
5006 false
5007 };
5008 if negated {
5009 self.advance();
5010 }
5011 if matches!(self.peek(), Token::Between) {
5012 expr = self.parse_between_tail(expr, negated)?;
5013 continue;
5014 }
5015 if matches!(self.peek(), Token::In) {
5016 expr = self.parse_in_tail(expr, negated)?;
5017 continue;
5018 }
5019 if matches!(self.peek(), Token::Like) {
5020 self.advance();
5021 let pattern = self.parse_expr(5)?;
5024 expr = Expr::Like {
5025 expr: Box::new(expr),
5026 pattern: Box::new(pattern),
5027 negated,
5028 };
5029 continue;
5030 }
5031 if matches!(self.peek(), Token::LBracket) {
5035 self.advance();
5036 let index = self.parse_expr(0)?;
5037 if !matches!(self.peek(), Token::RBracket) {
5038 return Err(self.err(alloc::format!(
5039 "expected ']' after array index, got {:?}",
5040 self.peek()
5041 )));
5042 }
5043 self.advance();
5044 expr = Expr::ArraySubscript {
5045 target: Box::new(expr),
5046 index: Box::new(index),
5047 };
5048 continue;
5049 }
5050 return Ok(expr);
5051 }
5052 }
5053
5054 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5058 self.advance(); let low = self.parse_expr(5)?;
5060 if !matches!(self.peek(), Token::And) {
5061 return Err(self.err(format!(
5062 "expected AND after BETWEEN low bound, got {:?}",
5063 self.peek()
5064 )));
5065 }
5066 self.advance();
5067 let high = self.parse_expr(5)?;
5068 let target = Box::new(expr);
5069 let combined = Expr::Binary {
5070 lhs: Box::new(Expr::Binary {
5071 lhs: target.clone(),
5072 op: BinOp::GtEq,
5073 rhs: Box::new(low),
5074 }),
5075 op: BinOp::And,
5076 rhs: Box::new(Expr::Binary {
5077 lhs: target,
5078 op: BinOp::LtEq,
5079 rhs: Box::new(high),
5080 }),
5081 };
5082 Ok(maybe_not(combined, negated))
5083 }
5084
5085 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
5090 let mut recursive = false;
5095 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5096 && s.eq_ignore_ascii_case("recursive")
5097 {
5098 self.advance();
5099 recursive = true;
5100 }
5101 let mut ctes = Vec::new();
5102 loop {
5103 let name = self.expect_ident_like()?;
5104 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
5108 self.advance();
5109 let mut names = Vec::new();
5110 loop {
5111 names.push(self.expect_ident_like()?);
5112 if matches!(self.peek(), Token::Comma) {
5113 self.advance();
5114 continue;
5115 }
5116 break;
5117 }
5118 if !matches!(self.peek(), Token::RParen) {
5119 return Err(self.err(format!(
5120 "expected ')' to close CTE column list, got {:?}",
5121 self.peek()
5122 )));
5123 }
5124 self.advance();
5125 names
5126 } else {
5127 Vec::new()
5128 };
5129 if !matches!(self.peek(), Token::As) {
5133 return Err(self.err(format!(
5134 "expected AS after CTE name {name:?}, got {:?}",
5135 self.peek()
5136 )));
5137 }
5138 self.advance();
5139 if !matches!(self.peek(), Token::LParen) {
5140 return Err(self.err(format!(
5141 "expected '(' after AS in WITH clause, got {:?}",
5142 self.peek()
5143 )));
5144 }
5145 self.advance();
5146 if !matches!(self.peek(), Token::Select) {
5147 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
5148 }
5149 let inner = self.parse_select_stmt()?;
5150 if !matches!(self.peek(), Token::RParen) {
5151 return Err(self.err(format!(
5152 "expected ')' after CTE body, got {:?}",
5153 self.peek()
5154 )));
5155 }
5156 self.advance();
5157 let Statement::Select(body) = inner else {
5158 unreachable!("parse_select_stmt returns Select")
5159 };
5160 ctes.push(crate::ast::Cte {
5161 name,
5162 body,
5163 recursive,
5164 column_overrides,
5165 });
5166 if matches!(self.peek(), Token::Comma) {
5167 self.advance();
5168 continue;
5169 }
5170 break;
5171 }
5172 if !matches!(self.peek(), Token::Select) {
5174 return Err(self.err(format!(
5175 "expected SELECT after WITH clause, got {:?}",
5176 self.peek()
5177 )));
5178 }
5179 let body_stmt = self.parse_select_stmt()?;
5180 let Statement::Select(mut body) = body_stmt else {
5181 unreachable!()
5182 };
5183 body.ctes = ctes;
5184 Ok(Statement::Select(body))
5185 }
5186
5187 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
5196 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
5200 None
5201 } else {
5202 Some(Box::new(self.parse_expr(0)?))
5203 };
5204 let mut branches: Vec<(Expr, Expr)> = Vec::new();
5205 loop {
5206 match self.peek() {
5207 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
5208 self.advance();
5209 let cond = self.parse_expr(0)?;
5210 match self.peek() {
5211 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
5212 self.advance();
5213 }
5214 other => {
5215 return Err(self.err(alloc::format!(
5216 "expected THEN after CASE WHEN <expr>, got {other:?}"
5217 )));
5218 }
5219 }
5220 let value = self.parse_expr(0)?;
5221 branches.push((cond, value));
5222 }
5223 _ => break,
5224 }
5225 }
5226 if branches.is_empty() {
5227 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
5228 }
5229 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
5230 {
5231 self.advance();
5232 Some(Box::new(self.parse_expr(0)?))
5233 } else {
5234 None
5235 };
5236 match self.peek() {
5237 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
5238 self.advance();
5239 }
5240 other => {
5241 return Err(self.err(alloc::format!(
5242 "expected END to close CASE expression, got {other:?}"
5243 )));
5244 }
5245 }
5246 Ok(Expr::Case {
5247 operand,
5248 branches,
5249 else_branch,
5250 })
5251 }
5252
5253 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
5254 if !matches!(self.peek(), Token::LParen) {
5255 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
5256 }
5257 self.advance();
5258 let inner = self.parse_select_stmt()?;
5259 if !matches!(self.peek(), Token::RParen) {
5260 return Err(self.err(format!(
5261 "expected ')' after EXISTS-subquery, got {:?}",
5262 self.peek()
5263 )));
5264 }
5265 self.advance();
5266 let Statement::Select(s) = inner else {
5267 unreachable!("parse_select_stmt returns Select")
5268 };
5269 Ok(Expr::Exists {
5270 subquery: Box::new(s),
5271 negated,
5272 })
5273 }
5274
5275 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5276 self.advance(); if !matches!(self.peek(), Token::LParen) {
5278 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
5279 }
5280 self.advance();
5281 if matches!(self.peek(), Token::Select) {
5283 let inner = self.parse_select_stmt()?;
5284 if !matches!(self.peek(), Token::RParen) {
5285 return Err(self.err(format!(
5286 "expected ')' after IN-subquery, got {:?}",
5287 self.peek()
5288 )));
5289 }
5290 self.advance();
5291 let Statement::Select(s) = inner else {
5292 unreachable!("parse_select_stmt always returns Statement::Select")
5293 };
5294 return Ok(Expr::InSubquery {
5295 expr: Box::new(expr),
5296 subquery: Box::new(s),
5297 negated,
5298 });
5299 }
5300 let mut elements = Vec::new();
5301 if !matches!(self.peek(), Token::RParen) {
5302 loop {
5303 elements.push(self.parse_expr(0)?);
5304 match self.peek() {
5305 Token::Comma => {
5306 self.advance();
5307 }
5308 Token::RParen => break,
5309 other => {
5310 return Err(
5311 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
5312 );
5313 }
5314 }
5315 }
5316 }
5317 self.advance(); let target = Box::new(expr);
5319 let combined = if elements.is_empty() {
5320 Expr::Literal(Literal::Bool(false))
5321 } else {
5322 let mut iter = elements.into_iter();
5323 let first = iter.next().unwrap();
5324 let mut acc = Expr::Binary {
5325 lhs: target.clone(),
5326 op: BinOp::Eq,
5327 rhs: Box::new(first),
5328 };
5329 for elt in iter {
5330 acc = Expr::Binary {
5331 lhs: Box::new(acc),
5332 op: BinOp::Or,
5333 rhs: Box::new(Expr::Binary {
5334 lhs: target.clone(),
5335 op: BinOp::Eq,
5336 rhs: Box::new(elt),
5337 }),
5338 };
5339 }
5340 acc
5341 };
5342 Ok(maybe_not(combined, negated))
5343 }
5344
5345 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
5353 if !matches!(self.peek(), Token::LParen) {
5354 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
5355 }
5356 self.advance();
5357 let field_name = self.expect_ident_like()?;
5358 let field = match field_name.to_ascii_lowercase().as_str() {
5359 "year" => ExtractField::Year,
5360 "month" => ExtractField::Month,
5361 "day" => ExtractField::Day,
5362 "hour" => ExtractField::Hour,
5363 "minute" => ExtractField::Minute,
5364 "second" => ExtractField::Second,
5365 "microsecond" | "microseconds" => ExtractField::Microsecond,
5366 other => {
5367 return Err(self.err(format!(
5368 "unknown EXTRACT field {other:?}; \
5369 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
5370 )));
5371 }
5372 };
5373 if !matches!(self.peek(), Token::From) {
5374 return Err(self.err(format!(
5375 "expected FROM after EXTRACT field, got {:?}",
5376 self.peek()
5377 )));
5378 }
5379 self.advance();
5380 let source = self.parse_expr(0)?;
5381 if !matches!(self.peek(), Token::RParen) {
5382 return Err(self.err(format!(
5383 "expected ')' to close EXTRACT, got {:?}",
5384 self.peek()
5385 )));
5386 }
5387 self.advance();
5388 Ok(Expr::Extract {
5389 field,
5390 source: Box::new(source),
5391 })
5392 }
5393
5394 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
5399 let tok = self.advance();
5400 let Token::String(text) = tok else {
5401 return Err(self.err(format!(
5402 "expected string literal after INTERVAL, got {tok:?}"
5403 )));
5404 };
5405 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
5406 message: format!(
5407 "cannot parse INTERVAL {text:?}; \
5408 expected `<n> <unit> [<n> <unit> ...]` with units \
5409 microsecond[s], millisecond[s], second[s], minute[s], \
5410 hour[s], day[s], week[s], month[s], year[s]"
5411 ),
5412 token_pos: self.pos.saturating_sub(1),
5413 })?;
5414 Ok(Expr::Literal(Literal::Interval {
5415 months,
5416 micros,
5417 text,
5418 }))
5419 }
5420
5421 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
5422 let mut elems = Vec::new();
5423 if matches!(self.peek(), Token::RBracket) {
5424 self.advance();
5425 return Ok(Expr::Literal(Literal::Vector(elems)));
5426 }
5427 loop {
5428 let e = self.parse_expr(0)?;
5429 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
5430 message: format!("vector element must be a numeric literal, got {e:?}"),
5431 token_pos: self.pos,
5432 })?;
5433 elems.push(x);
5434 match self.peek() {
5435 Token::Comma => {
5436 self.advance();
5437 }
5438 Token::RBracket => {
5439 self.advance();
5440 break;
5441 }
5442 other => {
5443 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
5444 }
5445 }
5446 }
5447 Ok(Expr::Literal(Literal::Vector(elems)))
5448 }
5449
5450 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
5459 let Token::Ident(s) = self.peek().clone() else {
5460 return NullTreatment::Respect;
5461 };
5462 let is_ignore = s.eq_ignore_ascii_case("ignore");
5463 let is_respect = s.eq_ignore_ascii_case("respect");
5464 if !is_ignore && !is_respect {
5465 return NullTreatment::Respect;
5466 }
5467 if self.pos + 1 < self.tokens.len()
5470 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
5471 && s2.eq_ignore_ascii_case("nulls")
5472 {
5473 self.advance();
5474 self.advance();
5475 return if is_ignore {
5476 NullTreatment::Ignore
5477 } else {
5478 NullTreatment::Respect
5479 };
5480 }
5481 NullTreatment::Respect
5482 }
5483
5484 #[allow(clippy::type_complexity)] fn parse_over_clause(
5487 &mut self,
5488 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
5489 if !matches!(self.peek(), Token::LParen) {
5490 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
5491 }
5492 self.advance();
5493 let mut partition_by = Vec::new();
5494 let mut order_by = Vec::new();
5495 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5497 && s.eq_ignore_ascii_case("partition")
5498 {
5499 self.advance();
5500 if !matches!(self.peek(), Token::By) {
5501 return Err(self.err(format!(
5502 "expected BY after PARTITION, got {:?}",
5503 self.peek()
5504 )));
5505 }
5506 self.advance();
5507 loop {
5508 partition_by.push(self.parse_expr(0)?);
5509 if matches!(self.peek(), Token::Comma) {
5510 self.advance();
5511 continue;
5512 }
5513 break;
5514 }
5515 }
5516 if matches!(self.peek(), Token::Order) {
5518 self.advance();
5519 if !matches!(self.peek(), Token::By) {
5520 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
5521 }
5522 self.advance();
5523 loop {
5524 let e = self.parse_expr(0)?;
5525 let desc = if matches!(self.peek(), Token::Desc) {
5526 self.advance();
5527 true
5528 } else if matches!(self.peek(), Token::Asc) {
5529 self.advance();
5530 false
5531 } else {
5532 false
5533 };
5534 order_by.push((e, desc));
5535 if matches!(self.peek(), Token::Comma) {
5536 self.advance();
5537 continue;
5538 }
5539 break;
5540 }
5541 }
5542 let mut frame: Option<WindowFrame> = None;
5546 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
5547 let kind = if s.eq_ignore_ascii_case("rows") {
5548 Some(FrameKind::Rows)
5549 } else if s.eq_ignore_ascii_case("range") {
5550 Some(FrameKind::Range)
5551 } else {
5552 None
5553 };
5554 if let Some(kind) = kind {
5555 self.advance();
5556 frame = Some(self.parse_frame_tail(kind)?);
5557 }
5558 }
5559 if !matches!(self.peek(), Token::RParen) {
5560 return Err(self.err(format!(
5561 "expected ')' to close OVER clause, got {:?}",
5562 self.peek()
5563 )));
5564 }
5565 self.advance();
5566 Ok((partition_by, order_by, frame))
5567 }
5568
5569 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
5575 if matches!(self.peek(), Token::Between) {
5576 self.advance();
5577 let start = self.parse_frame_bound()?;
5578 if !matches!(self.peek(), Token::And) {
5579 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
5580 }
5581 self.advance();
5582 let end = self.parse_frame_bound()?;
5583 Ok(WindowFrame {
5584 kind,
5585 start,
5586 end: Some(end),
5587 })
5588 } else {
5589 let start = self.parse_frame_bound()?;
5590 Ok(WindowFrame {
5591 kind,
5592 start,
5593 end: None,
5594 })
5595 }
5596 }
5597
5598 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
5601 if let Token::Integer(n) = *self.peek() {
5603 self.advance();
5604 let n: u64 = u64::try_from(n).map_err(|_| {
5605 self.err(format!(
5606 "invalid frame offset {n} — expected non-negative integer"
5607 ))
5608 })?;
5609 let dir = self.expect_ident_like()?;
5610 return if dir.eq_ignore_ascii_case("preceding") {
5611 Ok(FrameBound::OffsetPreceding(n))
5612 } else if dir.eq_ignore_ascii_case("following") {
5613 Ok(FrameBound::OffsetFollowing(n))
5614 } else {
5615 Err(self.err(format!(
5616 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
5617 )))
5618 };
5619 }
5620 let first = self.expect_ident_like()?;
5621 if first.eq_ignore_ascii_case("unbounded") {
5622 let dir = self.expect_ident_like()?;
5623 return if dir.eq_ignore_ascii_case("preceding") {
5624 Ok(FrameBound::UnboundedPreceding)
5625 } else if dir.eq_ignore_ascii_case("following") {
5626 Ok(FrameBound::UnboundedFollowing)
5627 } else {
5628 Err(self.err(format!(
5629 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
5630 )))
5631 };
5632 }
5633 if first.eq_ignore_ascii_case("current") {
5634 let row = self.expect_ident_like()?;
5635 if !row.eq_ignore_ascii_case("row") {
5636 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
5637 }
5638 return Ok(FrameBound::CurrentRow);
5639 }
5640 Err(self.err(format!(
5641 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
5642 )))
5643 }
5644
5645 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
5646 if matches!(self.peek(), Token::Dot) {
5647 self.advance();
5648 let name = self.expect_ident_like()?;
5649 if matches!(self.peek(), Token::LParen) {
5655 return self.finish_ident_atom(name);
5656 }
5657 return Ok(Expr::Column(ColumnName {
5658 qualifier: Some(first),
5659 name,
5660 }));
5661 }
5662 if matches!(self.peek(), Token::LParen) {
5663 self.advance();
5664 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
5668 self.advance();
5669 if !matches!(self.peek(), Token::RParen) {
5670 return Err(self.err(format!(
5671 "expected ')' after COUNT(*), got {:?}",
5672 self.peek()
5673 )));
5674 }
5675 self.advance();
5676 let null_treatment = self.parse_null_treatment_modifier();
5678 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5679 && s.eq_ignore_ascii_case("over")
5680 {
5681 self.advance();
5682 let (partition_by, order_by, frame) = self.parse_over_clause()?;
5683 return Ok(Expr::WindowFunction {
5684 name: "count_star".into(),
5685 args: Vec::new(),
5686 partition_by,
5687 order_by,
5688 frame,
5689 null_treatment,
5690 });
5691 }
5692 return Ok(Expr::FunctionCall {
5693 name: "count_star".into(),
5694 args: Vec::new(),
5695 });
5696 }
5697 let mut args = Vec::new();
5699 if !matches!(self.peek(), Token::RParen) {
5700 loop {
5701 args.push(self.parse_expr(0)?);
5702 match self.peek() {
5703 Token::Comma => {
5704 self.advance();
5705 }
5706 Token::RParen => break,
5707 other => {
5708 return Err(self.err(format!(
5709 "expected ',' or ')' in function args, got {other:?}"
5710 )));
5711 }
5712 }
5713 }
5714 }
5715 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
5723 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5724 && s.eq_ignore_ascii_case("over")
5725 {
5726 self.advance();
5727 let (partition_by, order_by, frame) = self.parse_over_clause()?;
5728 return Ok(Expr::WindowFunction {
5729 name: first,
5730 args,
5731 partition_by,
5732 order_by,
5733 frame,
5734 null_treatment,
5735 });
5736 }
5737 return Ok(Expr::FunctionCall { name: first, args });
5738 }
5739 let lc = first.to_ascii_lowercase();
5745 if matches!(
5746 lc.as_str(),
5747 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
5748 ) {
5749 return Ok(Expr::FunctionCall {
5750 name: lc,
5751 args: Vec::new(),
5752 });
5753 }
5754 Ok(Expr::Column(ColumnName {
5755 qualifier: None,
5756 name: first,
5757 }))
5758 }
5759}
5760
5761fn extract_first_column(expr: &Expr) -> Option<String> {
5769 match expr {
5770 Expr::Column(cn) => Some(cn.name.clone()),
5771 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
5772 Expr::Binary { lhs, rhs, .. } => {
5773 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
5774 }
5775 Expr::Unary { expr: e, .. } => extract_first_column(e),
5776 _ => None,
5777 }
5778}
5779
5780fn maybe_not(expr: Expr, negated: bool) -> Expr {
5781 if negated {
5782 Expr::Unary {
5783 op: UnOp::Not,
5784 expr: Box::new(expr),
5785 }
5786 } else {
5787 expr
5788 }
5789}
5790
5791fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
5792 let pair = match tok {
5793 Token::Or => (BinOp::Or, 1),
5794 Token::And => (BinOp::And, 2),
5795 Token::Eq => (BinOp::Eq, 4),
5796 Token::NotEq => (BinOp::NotEq, 4),
5797 Token::Lt => (BinOp::Lt, 4),
5798 Token::LtEq => (BinOp::LtEq, 4),
5799 Token::Gt => (BinOp::Gt, 4),
5800 Token::GtEq => (BinOp::GtEq, 4),
5801 Token::L2Distance => (BinOp::L2Distance, 5),
5804 Token::InnerProduct => (BinOp::InnerProduct, 5),
5805 Token::CosineDistance => (BinOp::CosineDistance, 5),
5806 Token::Plus => (BinOp::Add, 6),
5807 Token::Minus => (BinOp::Sub, 6),
5808 Token::Concat => (BinOp::Concat, 6),
5811 Token::Star => (BinOp::Mul, 7),
5812 Token::Slash => (BinOp::Div, 7),
5813 Token::JsonGet => (BinOp::JsonGet, 7),
5817 Token::JsonGetText => (BinOp::JsonGetText, 7),
5818 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
5819 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
5820 Token::JsonContains => (BinOp::JsonContains, 7),
5821 Token::TsMatch => (BinOp::TsMatch, 4),
5825 _ => return None,
5826 };
5827 Some(pair)
5828}
5829
5830#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
5831fn extract_numeric_literal(e: &Expr) -> Option<f32> {
5836 match e {
5837 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
5838 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
5839 Expr::Unary {
5840 op: UnOp::Neg,
5841 expr,
5842 } => extract_numeric_literal(expr).map(|x| -x),
5843 _ => None,
5844 }
5845}
5846
5847pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
5855 let parts: Vec<&str> = s.split_whitespace().collect();
5856 if parts.is_empty() || !parts.len().is_multiple_of(2) {
5857 return None;
5858 }
5859 let mut months: i32 = 0;
5860 let mut micros: i64 = 0;
5861 let mut i = 0;
5862 while i < parts.len() {
5863 let n: i64 = parts[i].parse().ok()?;
5864 let unit = parts[i + 1].to_ascii_lowercase();
5865 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
5866 match unit_stripped {
5867 "microsecond" => micros = micros.checked_add(n)?,
5868 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
5869 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
5870 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
5871 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
5872 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
5873 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
5874 "month" => {
5875 let n32 = i32::try_from(n).ok()?;
5876 months = months.checked_add(n32)?;
5877 }
5878 "year" => {
5879 let n32 = i32::try_from(n).ok()?;
5880 months = months.checked_add(n32.checked_mul(12)?)?;
5881 }
5882 _ => return None,
5883 }
5884 i += 2;
5885 }
5886 Some((months, micros))
5887}
5888
5889fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
5900 Some(match ident.to_ascii_lowercase().as_str() {
5901 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
5902 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
5903 "bigint" => ColumnTypeName::BigInt,
5904 "float" | "double" | "real" => ColumnTypeName::Float,
5905 "text" => ColumnTypeName::Text,
5906 "bool" | "boolean" => ColumnTypeName::Bool,
5907 "date" => ColumnTypeName::Date,
5908 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
5909 "timestamptz" => ColumnTypeName::Timestamptz,
5910 "json" => ColumnTypeName::Json,
5911 "jsonb" => ColumnTypeName::Jsonb,
5912 "bytea" | "bytes" => ColumnTypeName::Bytes,
5913 "tsvector" => ColumnTypeName::TsVector,
5914 "tsquery" => ColumnTypeName::TsQuery,
5915 _ => return None,
5916 })
5917}
5918
5919pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5946 parse_plpgsql_body(body)
5947}
5948
5949fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5950 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
5954 message: alloc::format!("plpgsql body lex error: {e}"),
5955 token_pos: 0,
5956 })?;
5957 let mut parser = Parser::new(tokens);
5958 parser.parse_plpgsql_block()
5959}
5960
5961#[cfg(test)]
5962mod tests {
5963 use super::*;
5964 use alloc::string::ToString;
5965
5966 fn parse(s: &str) -> Statement {
5967 parse_statement(s).expect("parse ok")
5968 }
5969
5970 fn lit_int(n: i64) -> Expr {
5971 Expr::Literal(Literal::Integer(n))
5972 }
5973
5974 fn col(name: &str) -> Expr {
5975 Expr::Column(ColumnName {
5976 qualifier: None,
5977 name: name.into(),
5978 })
5979 }
5980
5981 #[test]
5982 fn select_single_integer() {
5983 let s = parse("SELECT 1");
5984 let Statement::Select(s) = s else {
5985 panic!("expected SELECT")
5986 };
5987 assert_eq!(s.items.len(), 1);
5988 assert!(s.from.is_none());
5989 assert!(s.where_.is_none());
5990 }
5991
5992 #[test]
5993 fn select_multiple_literal_kinds() {
5994 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
5995 let Statement::Select(s) = s else {
5996 panic!("expected SELECT")
5997 };
5998 assert_eq!(s.items.len(), 5);
5999 }
6000
6001 #[test]
6002 fn select_wildcard_from_table() {
6003 let s = parse("SELECT * FROM users");
6004 let Statement::Select(s) = s else {
6005 panic!("expected SELECT")
6006 };
6007 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
6008 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
6009 }
6010
6011 #[test]
6012 fn select_with_table_alias() {
6013 let s = parse("SELECT * FROM users AS u");
6014 let Statement::Select(s) = s else {
6015 panic!("expected SELECT")
6016 };
6017 let t = &s.from.as_ref().unwrap().primary;
6018 assert_eq!(t.name, "users");
6019 assert_eq!(t.alias.as_deref(), Some("u"));
6020 }
6021
6022 #[test]
6023 fn select_with_where_eq() {
6024 let s = parse("SELECT a FROM t WHERE a = 1");
6025 let Statement::Select(s) = s else {
6026 panic!("expected SELECT")
6027 };
6028 let w = s.where_.unwrap();
6029 assert_eq!(
6030 w,
6031 Expr::Binary {
6032 lhs: Box::new(col("a")),
6033 op: BinOp::Eq,
6034 rhs: Box::new(lit_int(1)),
6035 }
6036 );
6037 }
6038
6039 #[test]
6040 fn arithmetic_precedence() {
6041 let s = parse("SELECT 1 + 2 * 3");
6042 let Statement::Select(s) = s else {
6043 panic!("expected SELECT")
6044 };
6045 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6046 panic!("wildcard?")
6047 };
6048 assert_eq!(
6049 expr,
6050 &Expr::Binary {
6051 lhs: Box::new(lit_int(1)),
6052 op: BinOp::Add,
6053 rhs: Box::new(Expr::Binary {
6054 lhs: Box::new(lit_int(2)),
6055 op: BinOp::Mul,
6056 rhs: Box::new(lit_int(3)),
6057 }),
6058 }
6059 );
6060 }
6061
6062 #[test]
6063 fn parentheses_override_precedence() {
6064 let s = parse("SELECT (1 + 2) * 3");
6065 let Statement::Select(s) = s else {
6066 panic!("expected SELECT")
6067 };
6068 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6069 panic!()
6070 };
6071 assert_eq!(
6072 expr,
6073 &Expr::Binary {
6074 lhs: Box::new(Expr::Binary {
6075 lhs: Box::new(lit_int(1)),
6076 op: BinOp::Add,
6077 rhs: Box::new(lit_int(2)),
6078 }),
6079 op: BinOp::Mul,
6080 rhs: Box::new(lit_int(3)),
6081 }
6082 );
6083 }
6084
6085 #[test]
6086 fn not_binds_below_comparison() {
6087 let s = parse("SELECT NOT a = 1 FROM t");
6089 let Statement::Select(s) = s else {
6090 panic!("expected SELECT")
6091 };
6092 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6093 panic!()
6094 };
6095 assert_eq!(
6096 expr,
6097 &Expr::Unary {
6098 op: UnOp::Not,
6099 expr: Box::new(Expr::Binary {
6100 lhs: Box::new(col("a")),
6101 op: BinOp::Eq,
6102 rhs: Box::new(lit_int(1)),
6103 }),
6104 }
6105 );
6106 }
6107
6108 #[test]
6109 fn unary_minus_binds_above_multiplication() {
6110 let s = parse("SELECT -a * 2 FROM t");
6112 let Statement::Select(s) = s else {
6113 panic!("expected SELECT")
6114 };
6115 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6116 panic!()
6117 };
6118 assert_eq!(
6119 expr,
6120 &Expr::Binary {
6121 lhs: Box::new(Expr::Unary {
6122 op: UnOp::Neg,
6123 expr: Box::new(col("a")),
6124 }),
6125 op: BinOp::Mul,
6126 rhs: Box::new(lit_int(2)),
6127 }
6128 );
6129 }
6130
6131 #[test]
6132 fn qualified_column() {
6133 let s = parse("SELECT t.col FROM t");
6134 let Statement::Select(s) = s else {
6135 panic!("expected SELECT")
6136 };
6137 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6138 panic!()
6139 };
6140 assert_eq!(
6141 expr,
6142 &Expr::Column(ColumnName {
6143 qualifier: Some("t".into()),
6144 name: "col".into()
6145 })
6146 );
6147 }
6148
6149 #[test]
6150 fn select_item_alias_with_as() {
6151 let s = parse("SELECT a AS y FROM t");
6152 let Statement::Select(s) = s else {
6153 panic!("expected SELECT")
6154 };
6155 let SelectItem::Expr { alias, .. } = &s.items[0] else {
6156 panic!()
6157 };
6158 assert_eq!(alias.as_deref(), Some("y"));
6159 }
6160
6161 #[test]
6162 fn trailing_semicolon_accepted() {
6163 let s = parse("SELECT 1;");
6164 let Statement::Select(s) = s else {
6165 panic!("expected SELECT")
6166 };
6167 assert_eq!(s.items.len(), 1);
6168 }
6169
6170 #[test]
6171 fn boolean_chain_with_and_or_not() {
6172 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
6174 let Statement::Select(s) = s else {
6175 panic!("expected SELECT")
6176 };
6177 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6178 panic!()
6179 };
6180 let expected = Expr::Binary {
6181 lhs: Box::new(Expr::Unary {
6182 op: UnOp::Not,
6183 expr: Box::new(col("a")),
6184 }),
6185 op: BinOp::Or,
6186 rhs: Box::new(Expr::Binary {
6187 lhs: Box::new(col("b")),
6188 op: BinOp::And,
6189 rhs: Box::new(Expr::Unary {
6190 op: UnOp::Not,
6191 expr: Box::new(col("c")),
6192 }),
6193 }),
6194 };
6195 assert_eq!(expr, &expected);
6196 }
6197
6198 #[test]
6199 fn empty_input_errors() {
6200 let err = parse_statement("").unwrap_err();
6201 assert!(err.message.contains("SELECT"));
6202 }
6203
6204 #[test]
6205 fn unmatched_paren_errors() {
6206 assert!(parse_statement("SELECT (1 + 2").is_err());
6207 }
6208
6209 #[test]
6210 fn display_round_trip_simple_select() {
6211 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
6212 let text = original.to_string();
6213 let again = parse_statement(&text).expect("re-parse");
6214 assert_eq!(original, again);
6215 }
6216
6217 #[test]
6220 fn create_table_single_column() {
6221 let s = parse("CREATE TABLE foo (a INT)");
6222 let Statement::CreateTable(c) = s else {
6223 panic!("expected CreateTable")
6224 };
6225 assert_eq!(c.name, "foo");
6226 assert_eq!(c.columns.len(), 1);
6227 assert_eq!(c.columns[0].name, "a");
6228 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6229 assert!(c.columns[0].nullable);
6230 }
6231
6232 #[test]
6233 fn create_table_multi_column_with_not_null_mix() {
6234 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
6235 let Statement::CreateTable(c) = s else {
6236 panic!()
6237 };
6238 assert_eq!(c.columns.len(), 4);
6239 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6240 assert!(!c.columns[0].nullable);
6241 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
6242 assert!(c.columns[1].nullable);
6243 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
6244 assert!(!c.columns[2].nullable);
6245 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
6246 }
6247
6248 #[test]
6249 fn create_table_bigint_supported() {
6250 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
6251 let Statement::CreateTable(c) = s else {
6252 panic!()
6253 };
6254 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
6255 }
6256
6257 #[test]
6258 fn create_table_vector_default_is_f32() {
6259 let s = parse("CREATE TABLE t (v VECTOR(128))");
6260 let Statement::CreateTable(c) = s else {
6261 panic!()
6262 };
6263 assert_eq!(
6264 c.columns[0].ty,
6265 ColumnTypeName::Vector {
6266 dim: 128,
6267 encoding: VecEncoding::F32,
6268 },
6269 );
6270 }
6271
6272 #[test]
6273 fn create_table_vector_using_sq8() {
6274 for sql in [
6277 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
6278 "CREATE TABLE t (v VECTOR(128) using sq8)",
6279 ] {
6280 let s = parse(sql);
6281 let Statement::CreateTable(c) = s else {
6282 panic!()
6283 };
6284 assert_eq!(
6285 c.columns[0].ty,
6286 ColumnTypeName::Vector {
6287 dim: 128,
6288 encoding: VecEncoding::Sq8,
6289 },
6290 "{sql}",
6291 );
6292 }
6293 }
6294
6295 #[test]
6296 fn create_table_vector_using_unknown_errors() {
6297 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
6298 assert!(
6299 err.message.contains("unknown vector encoding"),
6300 "got: {}",
6301 err.message
6302 );
6303 }
6304
6305 #[test]
6306 fn vector_using_sq8_display_roundtrips() {
6307 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
6310 let Statement::CreateTable(c) = s else {
6311 panic!()
6312 };
6313 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
6314 }
6315
6316 #[test]
6317 fn parser_recognises_placeholders() {
6318 use crate::ast::{Expr, SelectItem, Statement};
6319 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
6321 let Statement::Select(sel) = s else { panic!() };
6322 assert!(matches!(
6323 sel.items[0],
6324 SelectItem::Expr {
6325 expr: Expr::Placeholder(1),
6326 alias: None
6327 }
6328 ));
6329 let SelectItem::Expr {
6331 expr: Expr::Binary { lhs, rhs, .. },
6332 ..
6333 } = &sel.items[1]
6334 else {
6335 panic!()
6336 };
6337 assert!(matches!(**lhs, Expr::Placeholder(2)));
6338 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
6339 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
6341 panic!()
6342 };
6343 assert!(matches!(**rhs, Expr::Placeholder(3)));
6344 }
6345
6346 #[test]
6347 fn parser_rejects_dollar_zero() {
6348 assert!(parse_statement("SELECT $0").is_err());
6350 }
6351
6352 #[test]
6353 fn placeholder_display_roundtrips() {
6354 let s = parse("SELECT $42 FROM t");
6357 let printed = s.to_string();
6358 assert!(printed.contains("$42"));
6359 let again = parse(&printed);
6360 assert_eq!(s, again);
6361 }
6362
6363 #[test]
6364 fn alter_index_rebuild_bare() {
6365 use crate::ast::{AlterIndexTarget, Statement};
6366 let s = parse("ALTER INDEX my_idx REBUILD");
6367 let Statement::AlterIndex(a) = s else {
6368 panic!("expected AlterIndex, got {s:?}")
6369 };
6370 assert_eq!(a.name, "my_idx");
6371 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
6372 }
6373
6374 #[test]
6375 fn alter_index_rebuild_with_encoding() {
6376 use crate::ast::{AlterIndexTarget, Statement};
6377 for (sql, want) in [
6378 (
6379 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
6380 VecEncoding::F32,
6381 ),
6382 (
6383 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
6384 VecEncoding::Sq8,
6385 ),
6386 (
6387 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6388 VecEncoding::F16,
6389 ),
6390 ] {
6391 let s = parse(sql);
6392 let Statement::AlterIndex(a) = s else {
6393 panic!("{sql}: expected AlterIndex")
6394 };
6395 assert_eq!(a.name, "my_idx");
6396 assert_eq!(
6397 a.target,
6398 AlterIndexTarget::Rebuild {
6399 encoding: Some(want)
6400 },
6401 "{sql}"
6402 );
6403 }
6404 }
6405
6406 #[test]
6407 fn alter_index_rebuild_unknown_encoding_errors() {
6408 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
6409 assert!(
6410 err.message.contains("unknown vector encoding"),
6411 "got: {}",
6412 err.message
6413 );
6414 }
6415
6416 #[test]
6417 fn alter_index_rebuild_display_roundtrips() {
6418 for (input, want) in [
6419 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
6420 (
6421 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6422 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6423 ),
6424 (
6425 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6426 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6427 ),
6428 ] {
6429 let s = parse(input);
6430 assert_eq!(s.to_string(), want);
6431 }
6432 }
6433
6434 #[test]
6435 fn create_table_unknown_type_errors() {
6436 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
6439 assert!(err.message.contains("unsupported column type"));
6440 }
6441
6442 #[test]
6443 fn create_table_missing_table_keyword_errors() {
6444 assert!(parse_statement("CREATE x (a INT)").is_err());
6445 }
6446
6447 #[test]
6448 fn insert_single_value() {
6449 let s = parse("INSERT INTO foo VALUES (42)");
6450 let Statement::Insert(i) = s else {
6451 panic!("expected Insert")
6452 };
6453 assert_eq!(i.table, "foo");
6454 assert_eq!(i.rows.len(), 1);
6455 assert_eq!(i.rows[0].len(), 1);
6456 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
6457 }
6458
6459 #[test]
6460 fn insert_multi_value_with_mixed_literals() {
6461 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
6462 let Statement::Insert(i) = s else { panic!() };
6463 assert_eq!(i.rows.len(), 1);
6464 assert_eq!(i.rows[0].len(), 5);
6465 }
6466
6467 #[test]
6468 fn insert_missing_into_errors() {
6469 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
6470 }
6471
6472 #[test]
6473 fn create_table_round_trip() {
6474 let original =
6475 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
6476 let text = original.to_string();
6477 let again = parse_statement(&text).expect("re-parse");
6478 assert_eq!(original, again);
6479 }
6480
6481 #[test]
6482 fn insert_round_trip_with_negation_and_string() {
6483 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
6484 let text = original.to_string();
6485 let again = parse_statement(&text).expect("re-parse");
6486 assert_eq!(original, again);
6487 }
6488
6489 #[test]
6490 fn unknown_keyword_at_statement_start_errors() {
6491 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
6494 assert!(err.message.contains("expected SELECT"));
6495 }
6496
6497 #[test]
6500 fn create_index_basic() {
6501 let s = parse("CREATE INDEX idx_id ON users (id)");
6502 let Statement::CreateIndex(c) = s else {
6503 panic!("expected CreateIndex")
6504 };
6505 assert_eq!(c.name, "idx_id");
6506 assert_eq!(c.table, "users");
6507 assert_eq!(c.column, "id");
6508 }
6509
6510 #[test]
6511 fn create_index_missing_on_errors() {
6512 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
6513 }
6514
6515 #[test]
6516 fn create_index_missing_paren_errors() {
6517 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
6518 }
6519
6520 #[test]
6521 fn create_index_round_trip() {
6522 let original = parse("CREATE INDEX by_name ON users (name)");
6523 let again = parse_statement(&original.to_string()).unwrap();
6524 assert_eq!(original, again);
6525 }
6526
6527 #[test]
6530 fn create_unique_index_basic() {
6531 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
6532 let Statement::CreateIndex(c) = s else {
6533 panic!("expected CreateIndex");
6534 };
6535 assert!(c.is_unique);
6536 assert_eq!(c.column, "a");
6537 assert!(c.partial_predicate.is_none());
6538 }
6539
6540 #[test]
6541 fn create_unique_index_partial() {
6542 let s = parse(
6544 "CREATE UNIQUE INDEX idx_email_templates_user_default \
6545 ON email_templates (user_address) WHERE is_default = true",
6546 );
6547 let Statement::CreateIndex(c) = s else {
6548 panic!("expected CreateIndex");
6549 };
6550 assert!(c.is_unique);
6551 assert_eq!(c.table, "email_templates");
6552 assert_eq!(c.column, "user_address");
6553 assert!(c.partial_predicate.is_some());
6554 }
6555
6556 #[test]
6557 fn create_unique_index_composite_with_predicate() {
6558 let s = parse(
6560 "CREATE UNIQUE INDEX uq_calendar_events_instance \
6561 ON calendar_events (calendar_id, uid, recurrence_id) \
6562 WHERE recurrence_id IS NOT NULL",
6563 );
6564 let Statement::CreateIndex(c) = s else {
6565 panic!("expected CreateIndex");
6566 };
6567 assert!(c.is_unique);
6568 assert_eq!(c.column, "calendar_id");
6569 assert_eq!(
6570 c.extra_columns,
6571 vec!["uid".to_string(), "recurrence_id".to_string()]
6572 );
6573 assert!(c.partial_predicate.is_some());
6574 }
6575
6576 #[test]
6577 fn create_unique_index_using_btree_ok() {
6578 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
6579 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
6580 }
6581
6582 #[test]
6583 fn create_unique_index_using_hnsw_rejected() {
6584 let err =
6585 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
6586 assert!(err.message.contains("UNIQUE"), "{}", err.message);
6587 }
6588
6589 #[test]
6590 fn create_unique_index_round_trip() {
6591 let original = parse(
6592 "CREATE UNIQUE INDEX uq_calendar_events_master \
6593 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
6594 );
6595 let again = parse_statement(&original.to_string()).unwrap();
6596 assert_eq!(original, again);
6597 }
6598
6599 #[test]
6600 fn create_unique_without_index_errors() {
6601 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
6602 assert!(err.message.contains("INDEX"), "{}", err.message);
6603 }
6604
6605 #[test]
6608 fn create_table_bytea_column() {
6609 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
6610 let Statement::CreateTable(c) = s else {
6611 panic!("expected CreateTable");
6612 };
6613 assert_eq!(c.columns.len(), 2);
6614 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
6615 assert!(!c.columns[1].nullable);
6616 }
6617
6618 #[test]
6619 fn create_table_bytes_alias_column() {
6620 let s = parse("CREATE TABLE t (blob BYTES)");
6621 let Statement::CreateTable(c) = s else {
6622 panic!("expected CreateTable");
6623 };
6624 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
6625 }
6626
6627 #[test]
6628 fn bytea_round_trip_display() {
6629 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
6630 let again = parse_statement(&original.to_string()).unwrap();
6631 assert_eq!(original, again);
6632 }
6633
6634 #[test]
6637 fn begin_commit_rollback_parse_as_unit_variants() {
6638 assert_eq!(parse("BEGIN"), Statement::Begin);
6639 assert_eq!(parse("COMMIT"), Statement::Commit);
6640 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
6641 assert_eq!(parse("BEGIN;"), Statement::Begin);
6643 }
6644
6645 #[test]
6648 fn inner_product_binop_parses() {
6649 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
6650 let Statement::Select(s) = s else { panic!() };
6651 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6652 panic!()
6653 };
6654 assert!(matches!(
6655 expr,
6656 Expr::Binary {
6657 op: BinOp::InnerProduct,
6658 ..
6659 }
6660 ));
6661 }
6662
6663 #[test]
6664 fn cosine_distance_binop_parses() {
6665 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
6666 let Statement::Select(s) = s else { panic!() };
6667 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6668 panic!()
6669 };
6670 assert!(matches!(
6671 expr,
6672 Expr::Binary {
6673 op: BinOp::CosineDistance,
6674 ..
6675 }
6676 ));
6677 }
6678
6679 #[test]
6680 fn vector_cast_postfix_wraps_string_literal() {
6681 let s = parse("SELECT '[1,2,3]'::vector FROM t");
6682 let Statement::Select(s) = s else { panic!() };
6683 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6684 panic!()
6685 };
6686 assert!(matches!(
6687 expr,
6688 Expr::Cast {
6689 target: CastTarget::Vector,
6690 ..
6691 }
6692 ));
6693 }
6694
6695 #[test]
6696 fn unsupported_cast_target_errors() {
6697 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
6699 assert!(err.message.contains("unsupported cast target"));
6700 }
6701
6702 #[test]
6703 fn tx_statements_round_trip() {
6704 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
6705 let original = parse(q);
6706 let again = parse_statement(&original.to_string()).unwrap();
6707 assert_eq!(original, again);
6708 }
6709 }
6710
6711 #[test]
6712 fn interval_text_parsing_units() {
6713 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
6715 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
6716 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
6717 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
6718 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
6720 assert_eq!(
6721 parse_interval_text("1 day 2 hours"),
6722 Some((0, 86_400_000_000 + 7_200_000_000))
6723 );
6724 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
6726 assert_eq!(parse_interval_text(""), None);
6728 assert_eq!(parse_interval_text("garbage"), None);
6729 assert_eq!(parse_interval_text("1 fortnight"), None);
6730 assert_eq!(parse_interval_text("1"), None);
6731 }
6732
6733 #[test]
6734 fn interval_literal_roundtrips_via_display() {
6735 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
6736 let s = parsed.to_string();
6737 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
6739 let again = parse_statement(&s).unwrap();
6741 assert_eq!(parsed, again);
6742 }
6743
6744 #[test]
6747 fn parser_recognises_create_publication_bare() {
6748 let s = parse("CREATE PUBLICATION pub_a");
6749 let Statement::CreatePublication(p) = s else {
6750 panic!("expected CreatePublication, got {s:?}")
6751 };
6752 assert_eq!(p.name, "pub_a");
6753 assert_eq!(p.scope, PublicationScope::AllTables);
6754 }
6755
6756 #[test]
6757 fn parser_recognises_create_publication_for_all_tables() {
6758 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
6759 let Statement::CreatePublication(p) = s else {
6760 panic!("expected CreatePublication, got {s:?}")
6761 };
6762 assert_eq!(p.name, "pub_a");
6763 assert_eq!(p.scope, PublicationScope::AllTables);
6764 }
6765
6766 #[test]
6767 fn parser_recognises_drop_publication() {
6768 let s = parse("DROP PUBLICATION pub_a");
6769 let Statement::DropPublication(name) = s else {
6770 panic!("expected DropPublication, got {s:?}")
6771 };
6772 assert_eq!(name, "pub_a");
6773 }
6774
6775 #[test]
6776 fn parser_recognises_for_table_list() {
6777 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
6778 let Statement::CreatePublication(p) = s else {
6779 panic!("expected CreatePublication, got {s:?}")
6780 };
6781 assert_eq!(p.name, "pub_a");
6782 let PublicationScope::ForTables(ts) = p.scope else {
6783 panic!("expected ForTables scope")
6784 };
6785 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
6786 }
6787
6788 #[test]
6789 fn parser_recognises_for_tables_plural() {
6790 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
6792 let Statement::CreatePublication(p) = s else {
6793 panic!("expected CreatePublication, got {s:?}")
6794 };
6795 let PublicationScope::ForTables(ts) = p.scope else {
6796 panic!("expected ForTables")
6797 };
6798 assert_eq!(ts, alloc::vec!["t1", "t2"]);
6799 }
6800
6801 #[test]
6802 fn parser_recognises_for_all_tables_except_list() {
6803 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
6804 let Statement::CreatePublication(p) = s else {
6805 panic!()
6806 };
6807 let PublicationScope::AllTablesExcept(ts) = p.scope else {
6808 panic!("expected AllTablesExcept")
6809 };
6810 assert_eq!(ts, alloc::vec!["t1", "t2"]);
6811 }
6812
6813 #[test]
6814 fn parser_rejects_for_table_with_empty_list() {
6815 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
6817 .expect_err("must error on empty list");
6818 assert!(!err.message.is_empty());
6821 }
6822
6823 #[test]
6824 fn parser_recognises_show_publications() {
6825 let s = parse("SHOW PUBLICATIONS");
6828 assert!(matches!(s, Statement::ShowPublications));
6829 }
6830
6831 #[test]
6834 fn parser_recognises_create_subscription_single_publication() {
6835 let s = parse(
6836 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
6837 );
6838 let Statement::CreateSubscription(c) = s else {
6839 panic!("expected CreateSubscription, got {s:?}")
6840 };
6841 assert_eq!(c.name, "sub_a");
6842 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
6843 assert_eq!(c.publications, alloc::vec!["pub_a"]);
6844 }
6845
6846 #[test]
6847 fn parser_recognises_create_subscription_multi_publication() {
6848 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
6849 let Statement::CreateSubscription(c) = s else {
6850 panic!()
6851 };
6852 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
6853 }
6854
6855 #[test]
6856 fn parser_rejects_create_subscription_missing_connection() {
6857 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
6858 .expect_err("must error on missing CONNECTION");
6859 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
6860 }
6861
6862 #[test]
6863 fn parser_rejects_create_subscription_missing_publication() {
6864 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
6865 .expect_err("must error on missing PUBLICATION");
6866 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
6867 }
6868
6869 #[test]
6870 fn parser_recognises_drop_subscription() {
6871 let s = parse("DROP SUBSCRIPTION sub_a");
6872 let Statement::DropSubscription(name) = s else {
6873 panic!("expected DropSubscription, got {s:?}")
6874 };
6875 assert_eq!(name, "sub_a");
6876 }
6877
6878 #[test]
6879 fn parser_recognises_show_subscriptions() {
6880 let s = parse("SHOW SUBSCRIPTIONS");
6881 assert!(matches!(s, Statement::ShowSubscriptions));
6882 }
6883
6884 #[test]
6885 fn parser_recognises_wait_for_wal_position_no_timeout() {
6886 let s = parse("WAIT FOR WAL POSITION 12345");
6887 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6888 panic!("expected WaitForWalPosition, got {s:?}")
6889 };
6890 assert_eq!(pos, 12345);
6891 assert!(timeout_ms.is_none());
6892 }
6893
6894 #[test]
6895 fn parser_recognises_wait_for_wal_position_with_timeout() {
6896 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
6897 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6898 panic!()
6899 };
6900 assert_eq!(pos, 67890);
6901 assert_eq!(timeout_ms, Some(5000));
6902 }
6903
6904 #[test]
6905 fn parser_rejects_wait_with_negative_position() {
6906 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
6912 assert!(!err.message.is_empty());
6913 }
6914
6915 #[test]
6916 fn parser_recognises_bare_analyze() {
6917 let s = parse("ANALYZE");
6918 assert!(matches!(s, Statement::Analyze(None)));
6919 }
6920
6921 #[test]
6922 fn parser_recognises_analyze_with_table() {
6923 let s = parse("ANALYZE users");
6924 let Statement::Analyze(Some(name)) = s else {
6925 panic!("expected Analyze, got {s:?}")
6926 };
6927 assert_eq!(name, "users");
6928 }
6929
6930 #[test]
6931 fn parser_recognises_analyze_with_quoted_table() {
6932 let s = parse("ANALYZE \"Mixed Case\"");
6933 let Statement::Analyze(Some(name)) = s else {
6934 panic!()
6935 };
6936 assert_eq!(name, "Mixed Case");
6937 }
6938
6939 #[test]
6940 fn parser_rejects_analyze_with_garbage_token() {
6941 let err = parse_statement("ANALYZE 42").expect_err("must error");
6942 assert!(!err.message.is_empty());
6943 }
6944
6945 #[test]
6946 fn analyze_display_roundtrips() {
6947 for sql in ["ANALYZE", "ANALYZE users"] {
6948 let s = parse(sql);
6949 let printed = s.to_string();
6950 let again = parse_statement(&printed)
6951 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6952 assert_eq!(s, again);
6953 }
6954 }
6955
6956 #[test]
6957 fn wait_for_display_roundtrips() {
6958 for sql in [
6959 "WAIT FOR WAL POSITION 12345",
6960 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
6961 ] {
6962 let s = parse(sql);
6963 let printed = s.to_string();
6964 let again = parse_statement(&printed)
6965 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6966 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6967 }
6968 }
6969
6970 #[test]
6971 fn subscription_ddl_display_roundtrips() {
6972 for sql in [
6973 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
6974 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
6975 "DROP SUBSCRIPTION sub_a",
6976 "SHOW SUBSCRIPTIONS",
6977 ] {
6978 let s = parse(sql);
6979 let printed = s.to_string();
6980 let again = parse_statement(&printed)
6981 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6982 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6983 }
6984 }
6985
6986 #[test]
6987 fn parser_drop_dispatches_user_vs_publication() {
6988 let s = parse("DROP USER 'alice'");
6991 let Statement::DropUser(name) = s else {
6992 panic!("expected DropUser, got {s:?}")
6993 };
6994 assert_eq!(name, "alice");
6995 let s = parse("DROP PUBLICATION p1");
6997 assert!(matches!(s, Statement::DropPublication(_)));
6998 }
6999
7000 #[test]
7001 fn publication_ddl_display_roundtrips() {
7002 for sql in [
7005 "CREATE PUBLICATION pub_a",
7006 "CREATE PUBLICATION pub_a FOR ALL TABLES",
7007 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
7008 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
7009 "DROP PUBLICATION pub_a",
7010 "SHOW PUBLICATIONS",
7011 ] {
7012 let s = parse(sql);
7013 let printed = s.to_string();
7014 let again = parse_statement(&printed)
7015 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7016 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7017 }
7018 }
7019
7020 #[test]
7023 fn create_function_returns_trigger_plpgsql_minimal() {
7024 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
7025 let s = parse(sql);
7026 let Statement::CreateFunction(f) = s else {
7027 panic!("expected CreateFunction");
7028 };
7029 assert_eq!(f.name, "noop");
7030 assert!(!f.or_replace);
7031 assert!(f.args.is_empty());
7032 assert!(matches!(f.returns, FunctionReturn::Trigger));
7033 assert_eq!(f.language, "plpgsql");
7034 let FunctionBody::PlPgSql(block) = f.body else {
7035 panic!("expected PlPgSql body");
7036 };
7037 assert_eq!(block.statements.len(), 1);
7038 assert!(matches!(
7039 block.statements[0],
7040 PlPgSqlStmt::Return(ReturnTarget::New)
7041 ));
7042 }
7043
7044 #[test]
7045 fn create_function_or_replace_with_assignment() {
7046 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
7049BEGIN
7050 NEW.search_vector := to_tsvector('english', NEW.subject);
7051 RETURN NEW;
7052END;
7053$$";
7054 let s = parse(sql);
7055 let Statement::CreateFunction(f) = s else {
7056 panic!("expected CreateFunction");
7057 };
7058 assert!(f.or_replace);
7059 let FunctionBody::PlPgSql(block) = &f.body else {
7060 panic!("expected PlPgSql body");
7061 };
7062 assert_eq!(block.statements.len(), 2);
7063 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
7065 panic!("expected Assign as first stmt");
7066 };
7067 match target {
7068 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
7069 other => panic!("expected NEW.col, got {other:?}"),
7070 }
7071 assert!(matches!(
7073 block.statements[1],
7074 PlPgSqlStmt::Return(ReturnTarget::New)
7075 ));
7076 }
7077
7078 #[test]
7079 fn create_trigger_after_insert_or_update() {
7080 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
7081 let s = parse(sql);
7082 let Statement::CreateTrigger(t) = s else {
7083 panic!("expected CreateTrigger");
7084 };
7085 assert_eq!(t.name, "tg");
7086 assert_eq!(t.table, "messages");
7087 assert_eq!(t.timing, TriggerTiming::After);
7088 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
7089 assert_eq!(t.for_each, TriggerForEach::Row);
7090 assert_eq!(t.function, "update_sv");
7091 }
7092
7093 #[test]
7094 fn create_trigger_before_delete_execute_procedure_alias() {
7095 let sql =
7097 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
7098 let s = parse(sql);
7099 let Statement::CreateTrigger(t) = s else {
7100 panic!("expected CreateTrigger");
7101 };
7102 assert_eq!(t.timing, TriggerTiming::Before);
7103 assert_eq!(t.events, vec![TriggerEvent::Delete]);
7104 }
7105
7106 #[test]
7107 fn drop_trigger_if_exists_round_trips() {
7108 let s = Statement::DropTrigger {
7113 name: "tg".into(),
7114 table: "messages".into(),
7115 if_exists: true,
7116 };
7117 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
7118 }
7119
7120 #[test]
7121 fn trigger_ddl_display_roundtrips_through_parser() {
7122 for sql in [
7126 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
7127 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
7128 ] {
7129 let s = parse(sql);
7130 let printed = s.to_string();
7131 let again = parse_statement(&printed)
7132 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7133 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7134 }
7135 }
7136}