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!("expected identifier after '{first}.', got {other:?}"),
229 token_pos: self.pos.saturating_sub(1),
230 });
231 }
232 }
233 }
234 Ok(first)
235 }
236
237 #[allow(clippy::too_many_lines)]
238 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
239 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
247 return Ok(Statement::Empty);
248 }
249 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
259 let lc = s.to_ascii_lowercase();
260 if is_dump_noise_statement(&lc) {
261 self.consume_until_statement_boundary();
262 return Ok(Statement::Empty);
263 }
264 }
265 match self.peek() {
266 Token::Select => self.parse_select_stmt(),
267 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
283 self.advance();
284 let body_text = match self.advance() {
285 Token::String(s) => s,
286 other => {
287 return Err(self.err(alloc::format!(
288 "expected dollar-quoted body after DO, got {other:?}"
289 )));
290 }
291 };
292 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
294 self.advance();
295 let _ = self.expect_ident_like()?;
296 }
297 let block = parse_plpgsql_body(&body_text)?;
302 Ok(Statement::DoBlock(block))
303 }
304 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
308 self.advance();
309 self.parse_with_cte_then_select()
310 }
311 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
314 self.advance();
315 let mut analyze = false;
316 let mut suggest = false;
317 if matches!(self.peek(), Token::LParen) {
319 self.advance();
320 let opt = match self.peek().clone() {
321 Token::Ident(s) | Token::QuotedIdent(s) => s,
322 other => {
323 return Err(self.err(format!(
324 "expected option keyword inside EXPLAIN (…), got {other:?}"
325 )));
326 }
327 };
328 if !opt.eq_ignore_ascii_case("suggest") {
329 return Err(self.err(format!(
330 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
331 )));
332 }
333 self.advance();
334 if !matches!(self.peek(), Token::RParen) {
335 return Err(self.err(format!(
336 "expected ')' after EXPLAIN option, got {:?}",
337 self.peek()
338 )));
339 }
340 self.advance();
341 suggest = true;
342 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
343 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
344 {
345 self.advance();
346 analyze = true;
347 }
348 let inner = self.parse_select_stmt()?;
349 let Statement::Select(s) = inner else {
350 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
351 };
352 Ok(Statement::Explain(crate::ast::ExplainStatement {
353 analyze,
354 inner: Box::new(s),
355 suggest,
356 }))
357 }
358 Token::Create => self.parse_create_stmt(),
359 Token::Insert => self.parse_insert_stmt(),
360 Token::Begin => {
361 self.advance();
362 Ok(Statement::Begin)
363 }
364 Token::Commit => {
365 self.advance();
366 Ok(Statement::Commit)
367 }
368 Token::Rollback => {
369 self.advance();
370 if matches!(self.peek(), Token::To) {
374 self.advance();
375 if matches!(self.peek(), Token::Savepoint) {
376 self.advance();
377 }
378 let name = self.expect_ident_like()?;
379 Ok(Statement::RollbackToSavepoint(name))
380 } else {
381 Ok(Statement::Rollback)
382 }
383 }
384 Token::Savepoint => {
385 self.advance();
386 let name = self.expect_ident_like()?;
387 Ok(Statement::Savepoint(name))
388 }
389 Token::Release => {
390 self.advance();
391 if matches!(self.peek(), Token::Savepoint) {
394 self.advance();
395 }
396 let name = self.expect_ident_like()?;
397 Ok(Statement::ReleaseSavepoint(name))
398 }
399 Token::Show => {
400 self.advance();
401 let target = match self.advance() {
407 Token::Tables => "tables".to_string(),
408 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
409 other => {
410 return Err(self.err(format!(
411 "expected SHOW target, got {other:?}"
412 )));
413 }
414 };
415 match target.as_str() {
416 "tables" => Ok(Statement::ShowTables),
417 "users" => Ok(Statement::ShowUsers),
418 "publications" => Ok(Statement::ShowPublications),
423 "subscriptions" => Ok(Statement::ShowSubscriptions),
425 "columns" => {
426 if !matches!(self.peek(), Token::From) {
427 return Err(self.err(format!(
428 "expected FROM after SHOW COLUMNS, got {:?}",
429 self.peek()
430 )));
431 }
432 self.advance();
433 let table = self.expect_ident_like()?;
434 Ok(Statement::ShowColumns(table))
435 }
436 other => Err(self.err(format!(
437 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
438 ))),
439 }
440 }
441 Token::Drop => {
447 self.advance();
448 match self.peek() {
449 Token::Publication => {
450 self.advance();
451 let name = self.expect_ident_or_string()?;
452 Ok(Statement::DropPublication(name))
453 }
454 Token::Subscription => {
455 self.advance();
456 let name = self.expect_ident_or_string()?;
457 Ok(Statement::DropSubscription(name))
458 }
459 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
460 self.advance();
461 let name = self.expect_ident_or_string()?;
462 Ok(Statement::DropUser(name))
463 }
464 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
466 self.advance();
467 let if_exists = self.consume_if_exists();
468 let name = self.expect_ident_like()?;
469 if !matches!(self.peek(), Token::On) {
471 return Err(self.err(alloc::format!(
472 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
473 self.peek()
474 )));
475 }
476 self.advance();
477 let table = self.expect_ident_like()?;
478 Ok(Statement::DropTrigger {
479 name,
480 table,
481 if_exists,
482 })
483 }
484 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
488 self.advance();
489 let if_exists = self.consume_if_exists();
490 let name = self.expect_ident_like()?;
491 if matches!(self.peek(), Token::LParen) {
493 self.advance();
494 let mut depth = 1usize;
496 while depth > 0 {
497 match self.peek() {
498 Token::LParen => depth += 1,
499 Token::RParen => depth -= 1,
500 Token::Eof => {
501 return Err(self.err(alloc::format!(
502 "unterminated arg list in DROP FUNCTION {name:?}"
503 )));
504 }
505 _ => {}
506 }
507 self.advance();
508 }
509 }
510 Ok(Statement::DropFunction { name, if_exists })
511 }
512 Token::Table => {
520 self.advance();
521 let if_exists = self.consume_if_exists();
522 let mut names: Vec<String> = Vec::new();
523 loop {
524 names.push(self.expect_ident_like()?);
525 if matches!(self.peek(), Token::Comma) {
526 self.advance();
527 continue;
528 }
529 break;
530 }
531 if matches!(
532 self.peek(),
533 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
534 || s.eq_ignore_ascii_case("restrict")
535 ) {
536 self.advance();
537 }
538 Ok(Statement::DropTable { names, if_exists })
539 }
540 Token::Index => {
546 self.advance();
547 let if_exists = self.consume_if_exists();
548 let name = self.expect_ident_like()?;
549 if matches!(
550 self.peek(),
551 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
552 || s.eq_ignore_ascii_case("restrict")
553 ) {
554 self.advance();
555 }
556 Ok(Statement::DropIndex { name, if_exists })
557 }
558 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
563 self.advance();
564 let _ = self.consume_if_exists();
565 let _ = self.expect_ident_like()?;
566 if matches!(
567 self.peek(),
568 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
569 || s.eq_ignore_ascii_case("restrict")
570 ) {
571 self.advance();
572 }
573 Ok(Statement::Empty)
574 }
575 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
581 self.advance();
582 let _ = self.consume_if_exists();
583 let _ = self.expect_ident_like()?;
584 if matches!(
585 self.peek(),
586 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
587 || s.eq_ignore_ascii_case("restrict")
588 ) {
589 self.advance();
590 }
591 Ok(Statement::Empty)
592 }
593 other => Err(self.err(format!(
594 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
595 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
596 ))),
597 }
598 }
599 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
600 self.advance();
601 self.parse_update_after_keyword()
602 }
603 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
604 self.advance();
605 self.parse_delete_after_keyword()
606 }
607 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
611 self.advance();
612 self.parse_alter_after_keyword()
613 }
614 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
618 self.advance();
619 self.parse_wait_after_keyword()
620 }
621 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
632 self.advance();
633 let next = self.peek().clone();
634 let cold = match next {
635 Token::Ident(s) | Token::QuotedIdent(s) => s,
636 _ => {
637 return Err(
638 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
639 );
640 }
641 };
642 if !cold.eq_ignore_ascii_case("cold") {
643 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
644 }
645 self.advance();
646 let next = self.peek().clone();
647 let segments = match next {
648 Token::Ident(s) | Token::QuotedIdent(s) => s,
649 _ => {
650 return Err(self.err(format!(
651 "expected SEGMENTS after COMPACT COLD, got {:?}",
652 self.peek()
653 )));
654 }
655 };
656 if !segments.eq_ignore_ascii_case("segments") {
657 return Err(self.err(format!(
658 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
659 )));
660 }
661 self.advance();
662 Ok(Statement::CompactColdSegments)
663 }
664 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
665 self.advance();
666 let target = match self.peek() {
667 Token::Eof | Token::Semicolon => None,
668 Token::Ident(_) | Token::QuotedIdent(_) => {
669 Some(self.expect_ident_like()?)
670 }
671 other => {
672 return Err(self.err(format!(
673 "expected table name or end of statement after ANALYZE, got {other:?}"
674 )));
675 }
676 };
677 Ok(Statement::Analyze(target))
678 }
679 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
685 self.advance();
686 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"))
691 {
692 self.advance();
693 }
694 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
699 {
700 self.advance();
701 if matches!(
703 self.peek(),
704 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
705 ) {
706 self.advance();
707 }
708 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
710 {
711 self.advance();
712 if matches!(
713 self.peek(),
714 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
715 ) {
716 self.advance();
717 }
718 }
719 return Ok(Statement::Empty);
720 }
721 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
731 {
732 self.advance(); match self.peek().clone() {
734 Token::Default => {
735 self.advance();
736 }
737 Token::String(_)
738 | Token::Ident(_)
739 | Token::QuotedIdent(_) => {
740 self.advance();
741 }
742 other => {
743 return Err(self.err(alloc::format!(
744 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
745 )));
746 }
747 }
748 return Ok(Statement::Empty);
749 }
750 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
753 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
754 {
755 self.advance(); self.advance(); if matches!(
758 self.peek(),
759 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
760 ) {
761 self.advance();
762 }
763 return Ok(Statement::Empty);
764 }
765 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
770 loop {
771 let lhs = match self.peek().clone() {
772 Token::SessionVar(s) => {
773 self.advance();
774 s
775 }
776 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
777 other => {
778 return Err(self.err(format!(
779 "expected parameter name after SET, got {other:?}"
780 )));
781 }
782 };
783 match self.peek() {
785 Token::Eq => {
786 self.advance();
787 }
788 Token::To => {
789 self.advance();
790 }
791 other => {
792 return Err(self.err(format!(
793 "expected `=` or TO after SET {lhs}, got {other:?}"
794 )));
795 }
796 }
797 let value = self.parse_set_value()?;
798 pairs.push((lhs, value));
799 if matches!(self.peek(), Token::Comma) {
800 self.advance();
801 continue;
802 }
803 break;
804 }
805 if pairs.len() == 1 {
806 let (name, value) = pairs.into_iter().next().unwrap();
807 Ok(Statement::SetParameter { name, value })
808 } else {
809 Ok(Statement::SetParameterList(pairs))
810 }
811 }
812 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
814 self.advance();
815 match self.peek().clone() {
816 Token::All => {
817 self.advance();
818 Ok(Statement::ResetParameter(None))
819 }
820 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
821 self.advance();
822 Ok(Statement::ResetParameter(None))
823 }
824 _ => {
825 let name = self.parse_set_param_name()?;
826 Ok(Statement::ResetParameter(Some(name)))
827 }
828 }
829 }
830 other => Err(self.err(format!(
831 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
832 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
833 ))),
834 }
835 }
836
837 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
838 debug_assert!(matches!(self.peek(), Token::Create));
839 self.advance();
840 match self.peek() {
841 Token::Table => self.parse_create_table_stmt_after_create(),
842 Token::Index => self.parse_create_index_stmt_after_create(false),
843 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
850 self.advance();
851 if !matches!(self.peek(), Token::Index) {
852 return Err(self.err(alloc::format!(
853 "expected INDEX after CREATE UNIQUE, got {:?}",
854 self.peek()
855 )));
856 }
857 self.parse_create_index_stmt_after_create(true)
858 }
859 Token::Publication => {
860 self.advance();
861 self.parse_create_publication_after_keyword()
862 }
863 Token::Subscription => {
864 self.advance();
865 self.parse_create_subscription_after_keyword()
866 }
867 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
871 self.advance();
872 self.parse_create_user_after_keyword()
873 }
874 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
878 self.advance();
879 self.parse_create_extension_after_keyword()
880 }
881 Token::Or => {
887 self.advance();
888 let next = self.peek();
889 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
890 return Err(self.err(alloc::format!(
891 "expected REPLACE after CREATE OR, got {next:?}"
892 )));
893 };
894 if !s2.eq_ignore_ascii_case("replace") {
895 return Err(self.err(alloc::format!(
896 "expected REPLACE after CREATE OR, got {s2:?}"
897 )));
898 }
899 self.advance();
900 self.parse_create_function_or_trigger_after_or_replace(true)
901 }
902 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
903 self.advance();
904 self.parse_create_function_after_keyword(false)
905 }
906 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
907 self.advance();
908 self.parse_create_trigger_after_keyword(false)
909 }
910 Token::Ident(s) | Token::QuotedIdent(s)
916 if matches!(
917 s.to_ascii_lowercase().as_str(),
918 "sequence"
919 | "schema"
920 | "view"
921 | "materialized"
922 | "type"
923 | "domain"
924 | "database"
925 | "role"
926 | "policy"
927 | "operator"
928 | "cast"
929 | "rule"
930 | "aggregate"
931 | "language"
932 | "collation"
933 | "conversion"
934 ) =>
935 {
936 self.consume_until_statement_boundary();
937 Ok(Statement::Empty)
938 }
939 other => Err(self.err(format!(
940 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
941 ))),
942 }
943 }
944
945 fn parse_create_function_or_trigger_after_or_replace(
950 &mut self,
951 or_replace: bool,
952 ) -> Result<Statement, ParseError> {
953 let tok = self.peek();
954 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
955 return Err(self.err(alloc::format!(
956 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {tok:?}"
957 )));
958 };
959 if s.eq_ignore_ascii_case("function") {
960 self.advance();
961 self.parse_create_function_after_keyword(or_replace)
962 } else if s.eq_ignore_ascii_case("trigger") {
963 self.advance();
964 self.parse_create_trigger_after_keyword(or_replace)
965 } else {
966 Err(self.err(alloc::format!(
967 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {s:?}"
968 )))
969 }
970 }
971
972 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
977 self.consume_if_not_exists();
979 let name = self.expect_ident_like()?;
980 loop {
983 match self.peek() {
984 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
985 self.advance();
986 continue;
987 }
988 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
989 self.advance();
990 let _ = self.expect_ident_like()?;
991 continue;
992 }
993 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
994 self.advance();
995 let _ = self.advance();
997 continue;
998 }
999 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1000 self.advance();
1001 let _ = self.advance();
1002 continue;
1003 }
1004 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1005 self.advance();
1006 continue;
1007 }
1008 _ => break,
1009 }
1010 }
1011 Ok(Statement::CreateExtension(name))
1012 }
1013
1014 fn parse_create_function_after_keyword(
1026 &mut self,
1027 or_replace: bool,
1028 ) -> Result<Statement, ParseError> {
1029 let name = self.expect_ident_like()?;
1030 if !matches!(self.peek(), Token::LParen) {
1034 return Err(self.err(alloc::format!(
1035 "expected '(' after function name {name:?}, got {:?}",
1036 self.peek()
1037 )));
1038 }
1039 self.advance();
1040 let args = self.parse_function_arg_list()?;
1041 let tok = self.peek();
1043 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1044 return Err(self.err(alloc::format!(
1045 "expected RETURNS after function arg list, got {tok:?}"
1046 )));
1047 };
1048 if !s.eq_ignore_ascii_case("returns") {
1049 return Err(self.err(alloc::format!(
1050 "expected RETURNS after function arg list, got {s:?}"
1051 )));
1052 }
1053 self.advance();
1054 let returns = self.parse_function_return()?;
1055 let mut language: Option<String> = self.parse_optional_language()?;
1058 if !matches!(self.peek(), Token::As) {
1062 return Err(self.err(alloc::format!(
1063 "expected AS before function body, got {:?}",
1064 self.peek()
1065 )));
1066 }
1067 self.advance();
1068 let body_text = match self.peek() {
1069 Token::String(s) => {
1070 let body = s.clone();
1071 self.advance();
1072 body
1073 }
1074 other => {
1075 return Err(self.err(alloc::format!(
1076 "expected $$-quoted function body after AS, got {other:?}"
1077 )));
1078 }
1079 };
1080 if language.is_none() {
1082 language = self.parse_optional_language()?;
1083 }
1084 let language = language.unwrap_or_else(|| String::from("sql"));
1085 let body = if language.eq_ignore_ascii_case("plpgsql") {
1090 match parse_plpgsql_body(&body_text) {
1091 Ok(block) => FunctionBody::PlPgSql(block),
1092 Err(_) => FunctionBody::Raw(body_text),
1098 }
1099 } else {
1100 FunctionBody::Raw(body_text)
1101 };
1102 Ok(Statement::CreateFunction(CreateFunctionStatement {
1103 name,
1104 or_replace,
1105 args,
1106 returns,
1107 language,
1108 body,
1109 }))
1110 }
1111
1112 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1116 let mut args: Vec<FunctionArg> = Vec::new();
1117 if matches!(self.peek(), Token::RParen) {
1118 self.advance();
1119 return Ok(args);
1120 }
1121 loop {
1122 let mode = if matches!(self.peek(), Token::In) {
1125 self.advance();
1126 FunctionArgMode::In
1127 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1128 {
1129 self.advance();
1130 FunctionArgMode::Out
1131 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1132 {
1133 self.advance();
1134 FunctionArgMode::InOut
1135 } else {
1136 FunctionArgMode::In
1137 };
1138 let (name, ty_token) = {
1144 let first = self.expect_ident_like()?;
1145 match self.peek() {
1148 Token::Ident(_) | Token::QuotedIdent(_) => {
1149 let ty = self.expect_ident_like()?;
1150 (Some(first), ty)
1151 }
1152 _ => (None, first),
1153 }
1154 };
1155 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1157 Some(t) => FunctionArgType::Typed(t),
1158 None => FunctionArgType::Raw(ty_token),
1159 };
1160 args.push(FunctionArg { mode, name, ty });
1161 match self.peek() {
1162 Token::Comma => {
1163 self.advance();
1164 continue;
1165 }
1166 Token::RParen => {
1167 self.advance();
1168 return Ok(args);
1169 }
1170 other => {
1171 return Err(self.err(alloc::format!(
1172 "expected , or ) in function arg list, got {other:?}"
1173 )));
1174 }
1175 }
1176 }
1177 }
1178
1179 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1180 let ident = self.expect_ident_like()?;
1181 if ident.eq_ignore_ascii_case("trigger") {
1182 return Ok(FunctionReturn::Trigger);
1183 }
1184 if ident.eq_ignore_ascii_case("void") {
1185 return Ok(FunctionReturn::Void);
1186 }
1187 match map_type_ident_to_column_type_name(&ident) {
1188 Some(t) => Ok(FunctionReturn::Type(t)),
1189 None => Ok(FunctionReturn::Other(ident)),
1190 }
1191 }
1192
1193 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1194 match self.peek() {
1195 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1196 self.advance();
1197 let lang = self.expect_ident_like()?;
1198 Ok(Some(lang.to_ascii_lowercase()))
1199 }
1200 _ => Ok(None),
1201 }
1202 }
1203
1204 fn parse_create_trigger_after_keyword(
1208 &mut self,
1209 or_replace: bool,
1210 ) -> Result<Statement, ParseError> {
1211 let name = self.expect_ident_like()?;
1212 let timing = {
1213 let ident = self.expect_ident_like()?;
1214 if ident.eq_ignore_ascii_case("before") {
1215 TriggerTiming::Before
1216 } else if ident.eq_ignore_ascii_case("after") {
1217 TriggerTiming::After
1218 } else if ident.eq_ignore_ascii_case("instead") {
1219 let next = self.expect_ident_like()?;
1220 if !next.eq_ignore_ascii_case("of") {
1221 return Err(self.err(alloc::format!(
1222 "expected OF after INSTEAD in trigger timing, got {next:?}"
1223 )));
1224 }
1225 TriggerTiming::InsteadOf
1226 } else {
1227 return Err(self.err(alloc::format!(
1228 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
1229 )));
1230 }
1231 };
1232 let mut events: Vec<TriggerEvent> = Vec::new();
1239 let mut update_columns: Vec<String> = Vec::new();
1240 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
1241 events.push(first_ev);
1242 if !first_cols.is_empty() {
1243 update_columns = first_cols;
1244 }
1245 while matches!(self.peek(), Token::Or) {
1246 self.advance();
1247 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
1248 events.push(ev);
1249 if !cols.is_empty() {
1250 if !update_columns.is_empty() {
1251 return Err(
1252 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
1253 );
1254 }
1255 update_columns = cols;
1256 }
1257 }
1258 let tok = self.peek();
1260 let Token::On = tok else {
1261 return Err(self.err(alloc::format!(
1262 "expected ON after trigger events, got {tok:?}"
1263 )));
1264 };
1265 self.advance();
1266 let table = self.expect_ident_like()?;
1267 if !matches!(self.peek(), Token::For) {
1271 return Err(self.err(alloc::format!(
1272 "expected FOR EACH ROW / STATEMENT, got {:?}",
1273 self.peek()
1274 )));
1275 }
1276 self.advance();
1277 let for_each = {
1278 let e = self.expect_ident_like()?;
1279 if !e.eq_ignore_ascii_case("each") {
1280 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
1281 }
1282 let unit = self.expect_ident_like()?;
1283 if unit.eq_ignore_ascii_case("row") {
1284 TriggerForEach::Row
1285 } else if unit.eq_ignore_ascii_case("statement") {
1286 TriggerForEach::Statement
1287 } else {
1288 return Err(self.err(alloc::format!(
1289 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
1290 )));
1291 }
1292 };
1293 let exec = self.expect_ident_like()?;
1295 if !exec.eq_ignore_ascii_case("execute") {
1296 return Err(self.err(alloc::format!(
1297 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
1298 )));
1299 }
1300 let fn_or_proc = self.expect_ident_like()?;
1301 if !(fn_or_proc.eq_ignore_ascii_case("function")
1302 || fn_or_proc.eq_ignore_ascii_case("procedure"))
1303 {
1304 return Err(self.err(alloc::format!(
1305 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
1306 )));
1307 }
1308 let function = self.expect_ident_like()?;
1309 if matches!(self.peek(), Token::LParen) {
1311 self.advance();
1312 if !matches!(self.peek(), Token::RParen) {
1313 return Err(self.err(alloc::format!(
1314 "v7.12.4 trigger function calls take no args; got {:?}",
1315 self.peek()
1316 )));
1317 }
1318 self.advance();
1319 }
1320 Ok(Statement::CreateTrigger(CreateTriggerStatement {
1321 name,
1322 or_replace,
1323 timing,
1324 events,
1325 table,
1326 for_each,
1327 function,
1328 update_columns,
1329 }))
1330 }
1331
1332 fn parse_trigger_event_with_optional_of(
1336 &mut self,
1337 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
1338 let ev = self.parse_trigger_event()?;
1339 if !matches!(ev, TriggerEvent::Update) {
1340 return Ok((ev, Vec::new()));
1341 }
1342 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
1344 return Ok((ev, Vec::new()));
1345 }
1346 self.advance(); let mut cols: Vec<String> = Vec::new();
1348 loop {
1349 cols.push(self.expect_ident_like()?);
1350 if matches!(self.peek(), Token::Comma) {
1351 self.advance();
1352 continue;
1353 }
1354 break;
1355 }
1356 if cols.is_empty() {
1357 return Err(
1358 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
1359 );
1360 }
1361 Ok((ev, cols))
1362 }
1363
1364 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
1371 let declarations = if matches!(
1373 self.peek(),
1374 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
1375 ) {
1376 self.advance();
1377 self.parse_plpgsql_declare_block()?
1378 } else {
1379 Vec::new()
1380 };
1381 if !matches!(self.peek(), Token::Begin) {
1386 return Err(self.err(alloc::format!(
1387 "expected BEGIN at start of plpgsql block, got {:?}",
1388 self.peek()
1389 )));
1390 }
1391 self.advance();
1392 let statements = self.parse_plpgsql_stmt_list_until_end()?;
1393 Ok(PlPgSqlBlock {
1394 declarations,
1395 statements,
1396 })
1397 }
1398
1399 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
1403 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
1404 loop {
1405 if matches!(self.peek(), Token::Begin) {
1406 return Ok(out);
1407 }
1408 let name = self.expect_ident_like()?;
1409 let ty_token = self.expect_ident_like()?;
1410 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1411 Some(t) => FunctionArgType::Typed(t),
1412 None => FunctionArgType::Raw(ty_token),
1413 };
1414 let default = match self.peek() {
1415 Token::ColonEq => {
1416 self.advance();
1417 Some(self.parse_expr(0)?)
1418 }
1419 Token::Eq => {
1420 self.advance();
1424 Some(self.parse_expr(0)?)
1425 }
1426 _ => None,
1427 };
1428 if !matches!(self.peek(), Token::Semicolon) {
1430 return Err(self.err(alloc::format!(
1431 "expected ; after DECLARE entry for {name:?}, got {:?}",
1432 self.peek()
1433 )));
1434 }
1435 self.advance();
1436 out.push(PlPgSqlDeclare { name, ty, default });
1437 }
1438 }
1439
1440 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
1445 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
1446 loop {
1447 while matches!(self.peek(), Token::Semicolon) {
1449 self.advance();
1450 }
1451 if matches!(
1453 self.peek(),
1454 Token::Ident(s) | Token::QuotedIdent(s)
1455 if s.eq_ignore_ascii_case("end")
1456 || s.eq_ignore_ascii_case("else")
1457 || s.eq_ignore_ascii_case("elsif")
1458 || s.eq_ignore_ascii_case("elseif")
1459 ) {
1460 return Ok(statements);
1461 }
1462 let stmt = self.parse_plpgsql_stmt()?;
1465 statements.push(stmt);
1466 match self.peek() {
1467 Token::Semicolon => {
1468 self.advance();
1469 }
1470 Token::Ident(s) | Token::QuotedIdent(s)
1471 if s.eq_ignore_ascii_case("end")
1472 || s.eq_ignore_ascii_case("else")
1473 || s.eq_ignore_ascii_case("elsif")
1474 || s.eq_ignore_ascii_case("elseif") =>
1475 {
1476 }
1478 other => {
1479 return Err(self.err(alloc::format!(
1480 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
1481 )));
1482 }
1483 }
1484 }
1485 }
1486
1487 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1488 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
1490 {
1491 self.advance();
1492 return self.parse_plpgsql_return();
1493 }
1494 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
1496 {
1497 self.advance();
1498 return self.parse_plpgsql_if();
1499 }
1500 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
1502 {
1503 self.advance();
1504 return self.parse_plpgsql_raise();
1505 }
1506 if matches!(self.peek(), Token::Select)
1517 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
1518 {
1519 return Ok(PlPgSqlStmt::SelectInto {
1520 var: var_name,
1521 body: Box::new(select_body),
1522 });
1523 }
1524 if matches!(self.peek(), Token::Insert)
1534 || matches!(self.peek(), Token::Select)
1535 || matches!(self.peek(), Token::Create)
1536 || matches!(self.peek(), Token::Drop)
1537 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1538 if s.eq_ignore_ascii_case("update")
1539 || s.eq_ignore_ascii_case("delete")
1540 || s.eq_ignore_ascii_case("alter"))
1541 {
1542 let stmt = self.parse_one_statement()?;
1543 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
1544 }
1545 let target = self.parse_plpgsql_assign_target()?;
1548 match self.peek() {
1551 Token::ColonEq => {
1552 self.advance();
1553 }
1554 Token::Colon => {
1555 self.advance();
1556 if !matches!(self.peek(), Token::Eq) {
1557 return Err(self.err(alloc::format!(
1558 "expected := after plpgsql assign target, got `:` then {:?}",
1559 self.peek()
1560 )));
1561 }
1562 self.advance();
1563 }
1564 other => {
1565 return Err(self.err(alloc::format!(
1566 "expected := after plpgsql assign target, got {other:?}"
1567 )));
1568 }
1569 }
1570 let value = self.parse_expr(0)?;
1571 Ok(PlPgSqlStmt::Assign { target, value })
1572 }
1573
1574 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1577 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
1578 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
1579 loop {
1580 let cond = self.parse_expr(0)?;
1582 let then_kw = self.expect_ident_like()?;
1583 if !then_kw.eq_ignore_ascii_case("then") {
1584 return Err(self.err(alloc::format!(
1585 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
1586 )));
1587 }
1588 let body = self.parse_plpgsql_stmt_list_until_end()?;
1589 branches.push((cond, body));
1590 match self.peek() {
1592 Token::Ident(s) | Token::QuotedIdent(s)
1593 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
1594 {
1595 self.advance();
1596 continue;
1597 }
1598 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
1599 self.advance();
1600 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
1601 break;
1602 }
1603 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
1604 break;
1605 }
1606 other => {
1607 return Err(self.err(alloc::format!(
1608 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
1609 )));
1610 }
1611 }
1612 }
1613 let end_kw = self.expect_ident_like()?;
1616 if !end_kw.eq_ignore_ascii_case("end") {
1617 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
1618 }
1619 let if_kw = self.expect_ident_like()?;
1620 if !if_kw.eq_ignore_ascii_case("if") {
1621 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
1622 }
1623 Ok(PlPgSqlStmt::If {
1624 branches,
1625 else_branch,
1626 })
1627 }
1628
1629 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1633 let lvl_ident = self.expect_ident_like()?;
1634 let level = match lvl_ident.to_ascii_lowercase().as_str() {
1635 "notice" => RaiseLevel::Notice,
1636 "warning" => RaiseLevel::Warning,
1637 "info" => RaiseLevel::Info,
1638 "log" => RaiseLevel::Log,
1639 "debug" => RaiseLevel::Debug,
1640 "exception" => RaiseLevel::Exception,
1641 other => {
1642 return Err(self.err(alloc::format!(
1643 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
1644 )));
1645 }
1646 };
1647 let Token::String(msg) = self.peek() else {
1651 return Err(self.err(alloc::format!(
1652 "expected RAISE message string, got {:?}",
1653 self.peek()
1654 )));
1655 };
1656 let message = msg.clone();
1657 self.advance();
1658 let mut args: Vec<Expr> = Vec::new();
1660 while matches!(self.peek(), Token::Comma) {
1661 self.advance();
1662 args.push(self.parse_expr(0)?);
1663 }
1664 Ok(PlPgSqlStmt::Raise {
1665 level,
1666 message,
1667 args,
1668 })
1669 }
1670
1671 #[allow(clippy::too_many_lines)]
1679 fn try_parse_plpgsql_select_into(
1680 &mut self,
1681 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
1682 let start = self.pos;
1687 let mut into_pos: Option<usize> = None;
1688 let mut depth: i32 = 0;
1689 let mut i = start + 1;
1690 while i < self.tokens.len() {
1691 match &self.tokens[i] {
1692 Token::LParen => depth += 1,
1693 Token::RParen => depth -= 1,
1694 Token::Semicolon if depth == 0 => break,
1695 Token::Ident(s)
1696 if depth == 0
1697 && (s.eq_ignore_ascii_case("end")
1698 || s.eq_ignore_ascii_case("else")
1699 || s.eq_ignore_ascii_case("elsif")) =>
1700 {
1701 break;
1702 }
1703 Token::Into if depth == 0 => {
1704 into_pos = Some(i);
1705 break;
1706 }
1707 _ => {}
1708 }
1709 i += 1;
1710 }
1711 let Some(into_at) = into_pos else {
1712 return Ok(None);
1713 };
1714 let var = match self.tokens.get(into_at + 1) {
1718 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
1719 other => {
1720 return Err(self.err(alloc::format!(
1721 "expected variable name after SELECT … INTO, got {other:?}"
1722 )));
1723 }
1724 };
1725 let mut end = into_at + 2;
1728 let mut depth2: i32 = 0;
1729 while end < self.tokens.len() {
1730 match &self.tokens[end] {
1731 Token::LParen => depth2 += 1,
1732 Token::RParen => depth2 -= 1,
1733 Token::Semicolon if depth2 == 0 => break,
1734 Token::Ident(s)
1735 if depth2 == 0
1736 && (s.eq_ignore_ascii_case("end")
1737 || s.eq_ignore_ascii_case("else")
1738 || s.eq_ignore_ascii_case("elsif")) =>
1739 {
1740 break;
1741 }
1742 _ => {}
1743 }
1744 end += 1;
1745 }
1746 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
1751 for j in start..into_at {
1752 rebuilt.push(self.tokens[j].clone());
1753 }
1754 for j in (into_at + 2)..end {
1755 rebuilt.push(self.tokens[j].clone());
1756 }
1757 rebuilt.push(Token::Eof);
1758 let saved_pos = self.pos;
1759 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
1760 self.pos = 0;
1761 if !matches!(self.peek(), Token::Select) {
1763 self.tokens = saved_tokens;
1764 self.pos = saved_pos;
1765 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
1766 }
1767 let sel = self.parse_select_stmt();
1768 self.tokens = saved_tokens;
1769 self.pos = end;
1770 let sel = sel?;
1771 let Statement::Select(body) = sel else {
1772 return Err(self.err(alloc::format!(
1773 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
1774 )));
1775 };
1776 Ok(Some((body, var)))
1777 }
1778
1779 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
1780 let head = match self.advance() {
1794 Token::Ident(s) | Token::QuotedIdent(s) => s,
1795 other => {
1796 return Err(self.err(alloc::format!(
1797 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
1798 )));
1799 }
1800 };
1801 if matches!(self.peek(), Token::Dot) {
1802 self.advance();
1803 let col = self.expect_ident_like()?;
1804 if head.eq_ignore_ascii_case("new") {
1805 return Ok(AssignTarget::NewColumn(col));
1806 }
1807 if head.eq_ignore_ascii_case("old") {
1808 return Ok(AssignTarget::OldColumn(col));
1809 }
1810 return Err(self.err(alloc::format!(
1811 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
1812 got {head:?}.<col>"
1813 )));
1814 }
1815 Ok(AssignTarget::Local(head))
1816 }
1817
1818 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1819 match self.peek() {
1821 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
1822 self.advance();
1823 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
1824 }
1825 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
1826 self.advance();
1827 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
1828 }
1829 Token::Null => {
1830 self.advance();
1831 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1832 }
1833 Token::Semicolon => {
1836 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1837 }
1838 _ => {}
1839 }
1840 let e = self.parse_expr(0)?;
1842 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
1843 }
1844
1845 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
1846 if matches!(self.peek(), Token::Insert) {
1851 self.advance();
1852 return Ok(TriggerEvent::Insert);
1853 }
1854 match self.peek() {
1855 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1856 self.advance();
1857 Ok(TriggerEvent::Update)
1858 }
1859 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1860 self.advance();
1861 Ok(TriggerEvent::Delete)
1862 }
1863 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
1864 self.advance();
1865 Ok(TriggerEvent::Truncate)
1866 }
1867 other => Err(self.err(alloc::format!(
1868 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
1869 ))),
1870 }
1871 }
1872
1873 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
1880 let name = self.expect_ident_or_string()?;
1881 let scope = if matches!(self.peek(), Token::For) {
1884 self.advance();
1885 if matches!(self.peek(), Token::All) {
1886 self.advance();
1887 if !matches!(self.peek(), Token::Tables) {
1888 return Err(self.err(format!(
1889 "expected TABLES after FOR ALL, got {:?}",
1890 self.peek()
1891 )));
1892 }
1893 self.advance();
1894 if matches!(self.peek(), Token::Except) {
1895 self.advance();
1896 let tables = self.parse_publication_table_list()?;
1897 PublicationScope::AllTablesExcept(tables)
1898 } else {
1899 PublicationScope::AllTables
1900 }
1901 } else if matches!(self.peek(), Token::Table | Token::Tables) {
1902 self.advance();
1905 let tables = self.parse_publication_table_list()?;
1906 PublicationScope::ForTables(tables)
1907 } else {
1908 return Err(self.err(format!(
1909 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
1910 self.peek()
1911 )));
1912 }
1913 } else {
1914 PublicationScope::AllTables
1915 };
1916 Ok(Statement::CreatePublication(CreatePublicationStatement {
1917 name,
1918 scope,
1919 }))
1920 }
1921
1922 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
1927 let first = self.expect_ident_like()?;
1928 let mut out = alloc::vec![first];
1929 while matches!(self.peek(), Token::Comma) {
1930 self.advance();
1931 out.push(self.expect_ident_like()?);
1932 }
1933 Ok(out)
1934 }
1935
1936 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
1944 let name = self.expect_ident_or_string()?;
1945 if !matches!(self.peek(), Token::Connection) {
1946 return Err(self.err(format!(
1947 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
1948 self.peek()
1949 )));
1950 }
1951 self.advance();
1952 let conn_str = self.expect_string_literal()?;
1953 if !matches!(self.peek(), Token::Publication) {
1954 return Err(self.err(format!(
1955 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
1956 self.peek()
1957 )));
1958 }
1959 self.advance();
1960 let first = self.expect_ident_like()?;
1963 let mut publications = alloc::vec![first];
1964 while matches!(self.peek(), Token::Comma) {
1965 self.advance();
1966 publications.push(self.expect_ident_like()?);
1967 }
1968 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
1969 name,
1970 conn_str,
1971 publications,
1972 }))
1973 }
1974
1975 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
1982 let mut name = self.expect_ident_like()?;
1983 while matches!(self.peek(), Token::Dot) {
1984 self.advance();
1985 let next = self.expect_ident_like()?;
1986 name.push('.');
1987 name.push_str(&next);
1988 }
1989 Ok(name.to_ascii_lowercase())
1990 }
1991
1992 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
1993 match self.advance() {
1994 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
1995 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
1996 Ok(crate::ast::SetValue::Default)
1997 }
1998 Token::Ident(s) | Token::QuotedIdent(s) => {
1999 let mut accum = s;
2000 while matches!(self.peek(), Token::Dot) {
2001 self.advance();
2002 let next = self.expect_ident_like()?;
2003 accum.push('.');
2004 accum.push_str(&next);
2005 }
2006 Ok(crate::ast::SetValue::Ident(accum))
2007 }
2008 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
2009 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
2010 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
2016 Token::Minus => match self.advance() {
2021 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
2022 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
2023 other => Err(self.err(format!(
2024 "expected numeric after `-` in SET value, got {other:?}"
2025 ))),
2026 },
2027 other => Err(self.err(format!(
2028 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
2029 ))),
2030 }
2031 }
2032
2033 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
2034 if !matches!(self.peek(), Token::For) {
2038 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
2039 }
2040 self.advance();
2041 self.expect_keyword_ident("wal")?;
2042 self.expect_keyword_ident("position")?;
2043 let pos = self.expect_u64_literal()?;
2044 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
2045 {
2046 self.advance();
2047 self.expect_keyword_ident("timeout")?;
2048 Some(self.expect_u64_literal()?)
2049 } else {
2050 None
2051 };
2052 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
2053 }
2054
2055 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
2059 match self.advance() {
2060 Token::Integer(n) if n >= 0 => Ok(n as u64),
2061 Token::Integer(n) => Err(ParseError {
2062 message: format!("expected non-negative integer, got {n}"),
2063 token_pos: self.pos.saturating_sub(1),
2064 }),
2065 other => Err(ParseError {
2066 message: format!("expected integer literal, got {other:?}"),
2067 token_pos: self.pos.saturating_sub(1),
2068 }),
2069 }
2070 }
2071
2072 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
2076 let name = self.expect_ident_or_string()?;
2077 self.expect_keyword_ident("with")?;
2078 self.expect_keyword_ident("password")?;
2079 let password = self.expect_string_literal()?;
2080 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
2081 && s.eq_ignore_ascii_case("role")
2082 {
2083 self.advance();
2084 self.expect_string_literal()?
2085 } else {
2086 "readonly".to_string()
2087 };
2088 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
2089 name,
2090 password,
2091 role,
2092 }))
2093 }
2094
2095 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
2098 let table = self.expect_ident_like()?;
2099 self.expect_keyword_ident("set")?;
2100 let mut assignments = Vec::new();
2101 loop {
2102 let col = self.expect_ident_like()?;
2103 if !matches!(self.peek(), Token::Eq) {
2104 return Err(self.err(format!(
2105 "expected `=` after column name in UPDATE SET, got {:?}",
2106 self.peek()
2107 )));
2108 }
2109 self.advance();
2110 let value = self.parse_expr(0)?;
2111 assignments.push((col, value));
2112 if matches!(self.peek(), Token::Comma) {
2113 self.advance();
2114 continue;
2115 }
2116 break;
2117 }
2118 let where_ = if matches!(self.peek(), Token::Where) {
2119 self.advance();
2120 Some(self.parse_expr(0)?)
2121 } else {
2122 None
2123 };
2124 let returning = self.parse_optional_returning()?;
2125 Ok(Statement::Update(crate::ast::UpdateStatement {
2126 table,
2127 assignments,
2128 where_,
2129 returning,
2130 }))
2131 }
2132
2133 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
2136 if !matches!(self.peek(), Token::From) {
2137 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
2138 }
2139 self.advance();
2140 let table = self.expect_ident_like()?;
2141 let where_ = if matches!(self.peek(), Token::Where) {
2142 self.advance();
2143 Some(self.parse_expr(0)?)
2144 } else {
2145 None
2146 };
2147 let returning = self.parse_optional_returning()?;
2148 Ok(Statement::Delete(crate::ast::DeleteStatement {
2149 table,
2150 where_,
2151 returning,
2152 }))
2153 }
2154
2155 fn parse_optional_returning(
2160 &mut self,
2161 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
2162 let is_returning_kw = matches!(
2163 self.peek(),
2164 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
2165 );
2166 if !is_returning_kw {
2167 return Ok(None);
2168 }
2169 self.advance();
2170 let mut items = Vec::new();
2171 loop {
2172 items.push(self.parse_select_item()?);
2173 if matches!(self.peek(), Token::Comma) {
2174 self.advance();
2175 continue;
2176 }
2177 break;
2178 }
2179 Ok(Some(items))
2180 }
2181
2182 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
2190 match self.advance() {
2197 Token::Index => {}
2198 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
2199 Token::Table => {
2202 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2203 self.advance();
2204 }
2205 return self.parse_alter_table_after_keyword();
2206 }
2207 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
2208 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
2209 self.advance();
2210 }
2211 return self.parse_alter_table_after_keyword();
2212 }
2213 Token::Ident(s) | Token::QuotedIdent(s)
2219 if matches!(
2220 s.to_ascii_lowercase().as_str(),
2221 "sequence"
2222 | "view"
2223 | "function"
2224 | "type"
2225 | "domain"
2226 | "database"
2227 | "role"
2228 | "schema"
2229 | "owner"
2230 | "default"
2231 | "extension"
2232 | "materialized"
2233 | "policy"
2234 | "publication"
2235 | "subscription"
2236 ) =>
2237 {
2238 self.consume_until_statement_boundary();
2239 return Ok(Statement::Empty);
2240 }
2241 other => {
2242 return Err(self.err(format!(
2243 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
2244 after ALTER, got {other:?}"
2245 )));
2246 }
2247 }
2248 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2253 let next = self.tokens.get(self.pos + 1);
2254 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
2255 self.advance();
2256 self.advance();
2257 true
2258 } else {
2259 false
2260 }
2261 } else {
2262 false
2263 };
2264 let name = self.expect_ident_like()?;
2265 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
2269 self.advance();
2270 if matches!(self.peek(), Token::To) {
2271 self.advance();
2272 } else {
2273 self.expect_keyword_ident("to")?;
2274 }
2275 let new = self.expect_ident_like()?;
2276 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
2277 name,
2278 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
2279 }));
2280 }
2281 self.expect_keyword_ident("rebuild")?;
2283 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2285 self.advance();
2286 if !matches!(self.peek(), Token::LParen) {
2287 return Err(self.err(format!(
2288 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
2289 self.peek()
2290 )));
2291 }
2292 self.advance();
2293 self.expect_keyword_ident("encoding")?;
2294 if !matches!(self.peek(), Token::Eq) {
2295 return Err(self.err(format!(
2296 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
2297 self.peek()
2298 )));
2299 }
2300 self.advance();
2301 let enc_ident = match self.advance() {
2302 Token::Ident(s) | Token::QuotedIdent(s) => s,
2303 other => {
2304 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
2305 }
2306 };
2307 let enc = match enc_ident.to_ascii_lowercase().as_str() {
2308 "f32" => VecEncoding::F32,
2309 "sq8" => VecEncoding::Sq8,
2310 "half" => VecEncoding::F16,
2311 other => {
2312 return Err(self.err(format!(
2313 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
2314 )));
2315 }
2316 };
2317 if !matches!(self.peek(), Token::RParen) {
2318 return Err(self.err(format!(
2319 "expected ')' after encoding value, got {:?}",
2320 self.peek()
2321 )));
2322 }
2323 self.advance();
2324 Some(enc)
2325 } else {
2326 None
2327 };
2328 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
2329 name,
2330 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
2331 }))
2332 }
2333
2334 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
2340 let table_name = self.expect_ident_like()?;
2341 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
2342 loop {
2343 let subaction = self.parse_alter_table_subaction()?;
2344 targets.extend(subaction);
2348 if matches!(self.peek(), Token::Comma) {
2349 self.advance();
2350 continue;
2351 }
2352 break;
2353 }
2354 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
2355 name: table_name,
2356 targets,
2357 }))
2358 }
2359
2360 fn parse_alter_table_subaction(
2364 &mut self,
2365 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
2366 match self.peek() {
2367 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2368 self.advance();
2369 let setting = self.expect_ident_like()?;
2370 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
2371 return Err(self.err(alloc::format!(
2372 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
2373 )));
2374 }
2375 if !matches!(self.peek(), Token::Eq) {
2376 return Err(self.err(alloc::format!(
2377 "expected '=' after hot_tier_bytes, got {:?}",
2378 self.peek()
2379 )));
2380 }
2381 self.advance();
2382 let n = self.expect_u64_literal()?;
2383 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
2384 }
2385 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
2386 self.advance();
2387 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
2394 {
2395 let kind_pos = self.pos + 2;
2398 let kind = self.tokens.get(kind_pos).cloned();
2399 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
2400 {
2401 let fk = self.parse_table_level_fk()?;
2402 return Ok(alloc::vec![
2403 crate::ast::AlterTableTarget::AddForeignKey(fk)
2404 ]);
2405 }
2406 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
2407 {
2408 self.advance(); let _name = self.expect_ident_like()?;
2410 self.advance(); self.expect_keyword_ident("key")?;
2412 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2413 return Ok(alloc::vec![
2414 crate::ast::AlterTableTarget::AddTableConstraint(
2415 crate::ast::TableConstraint::PrimaryKey {
2416 name: None,
2417 columns: cols,
2418 }
2419 )
2420 ]);
2421 }
2422 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
2423 {
2424 self.advance(); let _name = self.expect_ident_like()?;
2426 self.advance(); let cols = self.parse_paren_ident_list("UNIQUE")?;
2428 return Ok(alloc::vec![
2429 crate::ast::AlterTableTarget::AddTableConstraint(
2430 crate::ast::TableConstraint::Unique {
2431 name: None,
2432 columns: cols,
2433 nulls_not_distinct: false,
2434 }
2435 )
2436 ]);
2437 }
2438 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
2439 {
2440 self.advance(); let _name = self.expect_ident_like()?;
2442 self.advance(); if !matches!(self.peek(), Token::LParen) {
2444 return Err(self.err(alloc::format!(
2445 "expected '(' after CHECK, got {:?}", self.peek()
2446 )));
2447 }
2448 self.advance();
2449 let expr = self.parse_expr(0)?;
2450 if matches!(self.peek(), Token::RParen) {
2451 self.advance();
2452 }
2453 return Ok(alloc::vec![
2454 crate::ast::AlterTableTarget::AddTableConstraint(
2455 crate::ast::TableConstraint::Check { name: None, expr }
2456 )
2457 ]);
2458 }
2459 }
2462 let is_fk = matches!(
2463 self.peek(),
2464 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2465 || s.eq_ignore_ascii_case("foreign")
2466 );
2467 if is_fk {
2468 let fk = self.parse_table_level_fk()?;
2469 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
2470 }
2471 match self.peek().clone() {
2474 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
2475 self.advance();
2476 self.expect_keyword_ident("key")?;
2477 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
2478 return Ok(alloc::vec![
2479 crate::ast::AlterTableTarget::AddTableConstraint(
2480 crate::ast::TableConstraint::PrimaryKey {
2481 name: None,
2482 columns: cols,
2483 }
2484 )
2485 ]);
2486 }
2487 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
2488 self.advance();
2489 let cols = self.parse_paren_ident_list("UNIQUE")?;
2490 return Ok(alloc::vec![
2491 crate::ast::AlterTableTarget::AddTableConstraint(
2492 crate::ast::TableConstraint::Unique {
2493 name: None,
2494 columns: cols,
2495 nulls_not_distinct: false,
2496 }
2497 )
2498 ]);
2499 }
2500 _ => {}
2501 }
2502 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2503 self.advance();
2504 }
2505 let mut if_not_exists = false;
2506 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2507 self.advance();
2508 if !matches!(self.peek(), Token::Not) {
2509 return Err(self.err(alloc::format!(
2510 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
2511 self.peek()
2512 )));
2513 }
2514 self.advance();
2515 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
2516 return Err(self.err(alloc::format!(
2517 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
2518 self.peek()
2519 )));
2520 }
2521 self.advance();
2522 if_not_exists = true;
2523 }
2524 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
2528 let col_name = column.name.clone();
2529 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
2530 column,
2531 if_not_exists,
2532 }];
2533 if let Some(mut fk) = col_level_fk {
2534 if fk.columns.is_empty() {
2535 fk.columns.push(col_name);
2536 }
2537 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
2538 }
2539 Ok(out)
2540 }
2541 Token::Drop => {
2542 self.advance();
2543 let subject = match self.peek() {
2550 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
2551 self.advance();
2552 "constraint"
2553 }
2554 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
2555 self.advance();
2556 "column"
2557 }
2558 Token::Ident(_) | Token::QuotedIdent(_) => "column",
2562 other => {
2563 return Err(self.err(alloc::format!(
2564 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
2565 )));
2566 }
2567 };
2568 let mut if_exists = false;
2569 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
2570 let n1 = self.tokens.get(self.pos + 1);
2571 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
2572 self.advance();
2573 self.advance();
2574 if_exists = true;
2575 }
2576 }
2577 let name = self.expect_ident_like()?;
2578 let mut cascade = false;
2579 if matches!(
2580 self.peek(),
2581 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
2582 || s.eq_ignore_ascii_case("restrict")
2583 ) {
2584 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
2585 {
2586 cascade = true;
2587 }
2588 self.advance();
2589 }
2590 if subject == "constraint" {
2591 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
2592 name,
2593 if_exists,
2594 }])
2595 } else {
2596 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
2597 column: name,
2598 if_exists,
2599 cascade,
2600 }])
2601 }
2602 }
2603 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
2604 self.advance();
2605 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2606 self.advance();
2607 }
2608 let col_name = self.expect_ident_like()?;
2609 match self.peek() {
2610 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
2611 self.advance();
2612 }
2613 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
2621 self.consume_until_statement_boundary();
2626 return Ok(Vec::new());
2627 }
2628 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
2629 self.consume_until_statement_boundary();
2631 return Ok(Vec::new());
2632 }
2633 other => {
2634 return Err(self.err(alloc::format!(
2635 "expected TYPE / SET / DROP after ALTER COLUMN <name>, got {other:?}"
2636 )));
2637 }
2638 }
2639 let new_type = self.parse_column_type_name()?;
2640 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
2641 {
2642 self.advance();
2643 Some(self.parse_expr(0)?)
2644 } else {
2645 None
2646 };
2647 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
2648 column: col_name,
2649 new_type,
2650 using,
2651 }])
2652 }
2653 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
2660 self.advance();
2661 if matches!(self.peek(), Token::To)
2666 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
2667 {
2668 self.advance();
2669 let new = self.expect_ident_like()?;
2670 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
2671 new,
2672 }]);
2673 }
2674 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
2675 self.advance();
2676 }
2677 let old = self.expect_ident_like()?;
2678 if matches!(self.peek(), Token::To) {
2681 self.advance();
2682 } else {
2683 self.expect_keyword_ident("to")?;
2684 }
2685 let new = self.expect_ident_like()?;
2686 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
2687 old,
2688 new,
2689 }])
2690 }
2691 Token::Ident(s)
2698 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
2699 {
2700 let enabled = s.eq_ignore_ascii_case("enable");
2701 self.advance();
2702 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
2708 return Err(self.err(alloc::format!(
2709 "expected TRIGGER after {}, got {:?}",
2710 if enabled { "ENABLE" } else { "DISABLE" },
2711 self.peek()
2712 )));
2713 }
2714 self.advance();
2715 let which = if matches!(self.peek(), Token::All)
2718 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
2719 {
2720 self.advance();
2721 crate::ast::TriggerSelector::All
2722 } else {
2723 let name = self.expect_ident_like()?;
2724 crate::ast::TriggerSelector::Named(name)
2725 };
2726 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
2727 which,
2728 enabled,
2729 }])
2730 }
2731 other => Err(self.err(alloc::format!(
2732 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
2733 ))),
2734 }
2735 }
2736
2737 fn try_peek_meta_qualified(&mut self) -> Option<String> {
2746 let schema = match self.tokens.get(self.pos) {
2748 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
2749 _ => return None,
2750 };
2751 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
2753 return None;
2754 }
2755 let tbl = match self.tokens.get(self.pos + 2)? {
2759 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
2760 Token::Tables => "tables".to_string(),
2761 _ => return None,
2764 };
2765 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
2769 ("__spg_info_", tbl.to_ascii_lowercase())
2770 } else if schema.eq_ignore_ascii_case("pg_catalog") {
2771 let bare = tbl
2772 .to_ascii_lowercase()
2773 .strip_prefix("pg_")
2774 .map(alloc::string::String::from)
2775 .unwrap_or_else(|| tbl.to_ascii_lowercase());
2776 ("__spg_pg_", bare)
2777 } else {
2778 return None;
2779 };
2780 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
2784 }
2785
2786 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
2788 match self.advance() {
2789 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
2790 other => Err(ParseError {
2791 message: format!("expected {kw:?}, got {other:?}"),
2792 token_pos: self.pos.saturating_sub(1),
2793 }),
2794 }
2795 }
2796
2797 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
2801 match self.advance() {
2802 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
2803 other => Err(ParseError {
2804 message: format!("expected identifier or string, got {other:?}"),
2805 token_pos: self.pos.saturating_sub(1),
2806 }),
2807 }
2808 }
2809
2810 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
2811 match self.advance() {
2812 Token::String(s) => Ok(s),
2813 other => Err(ParseError {
2814 message: format!("expected quoted string, got {other:?}"),
2815 token_pos: self.pos.saturating_sub(1),
2816 }),
2817 }
2818 }
2819
2820 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
2821 let mut head = self.parse_bare_select()?;
2826 while matches!(self.peek(), Token::Union) {
2827 self.advance();
2828 let kind = if matches!(self.peek(), Token::All) {
2829 self.advance();
2830 UnionKind::All
2831 } else {
2832 UnionKind::Distinct
2833 };
2834 let peer = self.parse_bare_select()?;
2835 head.unions.push((kind, peer));
2836 }
2837 head.order_by = if matches!(self.peek(), Token::Order) {
2838 self.advance();
2839 if !matches!(self.peek(), Token::By) {
2840 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
2841 }
2842 self.advance();
2843 let mut keys = Vec::new();
2846 loop {
2847 let expr = self.parse_expr(0)?;
2848 let desc = if matches!(self.peek(), Token::Desc) {
2849 self.advance();
2850 true
2851 } else if matches!(self.peek(), Token::Asc) {
2852 self.advance();
2853 false
2854 } else {
2855 false
2856 };
2857 keys.push(OrderBy { expr, desc });
2858 if matches!(self.peek(), Token::Comma) {
2859 self.advance();
2860 } else {
2861 break;
2862 }
2863 }
2864 keys
2865 } else {
2866 Vec::new()
2867 };
2868 head.limit = if matches!(self.peek(), Token::Limit) {
2869 self.advance();
2870 Some(self.parse_limit_expr("LIMIT")?)
2871 } else {
2872 None
2873 };
2874 head.offset = if matches!(self.peek(), Token::Offset) {
2875 self.advance();
2876 Some(self.parse_limit_expr("OFFSET")?)
2877 } else {
2878 None
2879 };
2880 Ok(Statement::Select(head))
2881 }
2882
2883 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2888 match self.advance() {
2889 Token::Integer(n) if n >= 0 => u32::try_from(n)
2890 .map(crate::ast::LimitExpr::Literal)
2891 .map_err(|_| ParseError {
2892 message: alloc::format!("{label} value too large: {n}"),
2893 token_pos: self.pos.saturating_sub(1),
2894 }),
2895 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2896 other => Err(ParseError {
2897 message: alloc::format!(
2898 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2899 ),
2900 token_pos: self.pos.saturating_sub(1),
2901 }),
2902 }
2903 }
2904
2905 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2910 if !matches!(self.peek(), Token::Select) {
2911 return Err(self.err(format!(
2912 "expected SELECT to start a query block, got {:?}",
2913 self.peek()
2914 )));
2915 }
2916 self.advance();
2917 let distinct = if matches!(self.peek(), Token::Distinct) {
2918 self.advance();
2919 true
2920 } else {
2921 false
2922 };
2923 let items = self.parse_select_list()?;
2924 let from = if matches!(self.peek(), Token::From) {
2925 self.advance();
2926 Some(self.parse_from_clause()?)
2927 } else {
2928 None
2929 };
2930 let where_ = if matches!(self.peek(), Token::Where) {
2931 self.advance();
2932 Some(self.parse_expr(0)?)
2933 } else {
2934 None
2935 };
2936 let mut group_by_all = false;
2937 let group_by = if matches!(self.peek(), Token::Group) {
2938 self.advance();
2939 if !matches!(self.peek(), Token::By) {
2940 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2941 }
2942 self.advance();
2943 if matches!(self.peek(), Token::All) {
2946 self.advance();
2947 group_by_all = true;
2948 None
2949 } else {
2950 let mut groups = Vec::new();
2951 loop {
2952 groups.push(self.parse_expr(0)?);
2953 if matches!(self.peek(), Token::Comma) {
2954 self.advance();
2955 } else {
2956 break;
2957 }
2958 }
2959 Some(groups)
2960 }
2961 } else {
2962 None
2963 };
2964 let having = if matches!(self.peek(), Token::Having) {
2965 self.advance();
2966 Some(self.parse_expr(0)?)
2967 } else {
2968 None
2969 };
2970 Ok(SelectStatement {
2971 ctes: Vec::new(),
2972 distinct,
2973 items,
2974 from,
2975 where_,
2976 group_by,
2977 group_by_all,
2978 having,
2979 unions: Vec::new(),
2980 order_by: Vec::new(),
2981 limit: None,
2982 offset: None,
2983 })
2984 }
2985
2986 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2987 debug_assert!(matches!(self.peek(), Token::Table));
2989 self.advance();
2990 let if_not_exists = self.consume_if_not_exists();
2991 let name = self.expect_ident_like()?;
2992 if !matches!(self.peek(), Token::LParen) {
2993 return Err(self.err(format!(
2994 "expected '(' after table name, got {:?}",
2995 self.peek()
2996 )));
2997 }
2998 self.advance();
2999 let mut columns = Vec::new();
3000 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
3001 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
3002 loop {
3003 if self.peek_table_level_pk_start() {
3009 table_constraints.push(self.parse_table_level_primary_key()?);
3010 } else if self.peek_table_level_unique_start() {
3011 table_constraints.push(self.parse_table_level_unique()?);
3012 } else if self.peek_table_level_check_start() {
3013 table_constraints.push(self.parse_table_level_check()?);
3015 } else if self.peek_mysql_inline_key_start() {
3016 if let Some(uc) = self.parse_mysql_inline_key()? {
3022 table_constraints.push(uc);
3023 }
3024 } else if self.peek_constraint_or_fk_start() {
3025 foreign_keys.push(self.parse_table_level_fk()?);
3026 } else {
3027 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
3028 if col.is_unique {
3032 table_constraints.push(crate::ast::TableConstraint::Unique {
3033 name: None,
3034 columns: alloc::vec![col.name.clone()],
3035 nulls_not_distinct: false,
3036 });
3037 }
3038 if let Some(check_expr) = col.check.clone() {
3039 table_constraints.push(crate::ast::TableConstraint::Check {
3040 name: None,
3041 expr: check_expr,
3042 });
3043 }
3044 columns.push(col);
3045 if let Some(fk) = col_level_fk {
3046 foreign_keys.push(fk);
3047 }
3048 }
3049 match self.peek() {
3050 Token::Comma => {
3051 self.advance();
3052 }
3053 Token::RParen => {
3054 self.advance();
3055 break;
3056 }
3057 other => {
3058 return Err(
3059 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
3060 );
3061 }
3062 }
3063 }
3064 if columns.is_empty() {
3065 return Err(self.err("CREATE TABLE requires at least one column".into()));
3066 }
3067 self.consume_mysql_table_options();
3074 Ok(Statement::CreateTable(CreateTableStatement {
3075 name,
3076 columns,
3077 if_not_exists,
3078 foreign_keys,
3079 table_constraints,
3080 }))
3081 }
3082
3083 fn peek_mysql_inline_key_start(&self) -> bool {
3092 let cur = self.peek();
3093 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
3103 match self.tokens.get(skip) {
3106 Some(Token::LParen) => true,
3107 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
3108 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
3109 }
3110 _ => false,
3111 }
3112 };
3113 let is_key_or_index_tok = |t: &Token| -> bool {
3117 matches!(t, Token::Index)
3118 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
3119 };
3120 match cur {
3121 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
3122 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
3123 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
3124 }
3125 Token::Ident(s)
3126 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
3127 {
3128 let nxt = self.tokens.get(self.pos + 1);
3129 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
3130 self.pos + 2
3131 } else {
3132 self.pos + 1
3133 };
3134 after_keyword_followed_by_paren_or_ident_paren(after_after)
3135 }
3136 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
3137 let nxt = self.tokens.get(self.pos + 1);
3138 if !nxt.is_some_and(is_key_or_index_tok) {
3139 return false;
3140 }
3141 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
3142 }
3143 _ => false,
3144 }
3145 }
3146
3147 fn parse_mysql_inline_key(
3156 &mut self,
3157 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
3158 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
3160 {
3161 self.advance();
3162 true
3163 } else {
3164 false
3165 };
3166 let is_fulltext_or_spatial = if matches!(
3171 self.peek(),
3172 Token::Ident(s) if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial")
3173 ) {
3174 self.advance();
3175 true
3176 } else {
3177 false
3178 };
3179 match self.peek() {
3182 Token::Index => {
3183 self.advance();
3184 }
3185 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
3186 self.advance();
3187 }
3188 other => {
3189 return Err(self.err(alloc::format!(
3190 "expected KEY/INDEX in inline index declaration, got {other:?}"
3191 )));
3192 }
3193 }
3194 let mut idx_name: Option<String> = None;
3199 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
3200 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
3201 {
3202 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
3203 idx_name = Some(s);
3204 }
3205 }
3206 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3208 self.advance();
3209 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
3210 self.advance();
3211 }
3212 }
3213 if !matches!(self.peek(), Token::LParen) {
3215 return Err(self.err(alloc::format!(
3216 "expected '(' in inline KEY/INDEX, got {:?}",
3217 self.peek()
3218 )));
3219 }
3220 self.advance();
3221 let mut cols: Vec<String> = Vec::new();
3222 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
3223 self.advance();
3224 cols.push(s);
3225 if matches!(self.peek(), Token::LParen) {
3227 let mut depth = 1usize;
3228 self.advance();
3229 while depth > 0 {
3230 match self.peek() {
3231 Token::LParen => depth += 1,
3232 Token::RParen => depth -= 1,
3233 Token::Eof => break,
3234 _ => {}
3235 }
3236 self.advance();
3237 }
3238 }
3239 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
3241 || matches!(self.peek(), Token::Asc | Token::Desc)
3242 {
3243 self.advance();
3244 }
3245 if matches!(self.peek(), Token::Comma) {
3246 self.advance();
3247 continue;
3248 }
3249 break;
3250 }
3251 if matches!(self.peek(), Token::RParen) {
3252 self.advance();
3253 }
3254 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
3257 self.advance();
3258 }
3259 if cols.is_empty() {
3260 return Ok(None);
3261 }
3262 if is_unique {
3263 Ok(Some(crate::ast::TableConstraint::Unique {
3269 name: idx_name,
3270 columns: cols,
3271 nulls_not_distinct: false,
3272 }))
3273 } else if is_fulltext_or_spatial {
3274 Ok(None)
3276 } else {
3277 Ok(Some(crate::ast::TableConstraint::Index {
3280 name: idx_name,
3281 columns: cols,
3282 }))
3283 }
3284 }
3285
3286 fn consume_mysql_table_options(&mut self) {
3291 loop {
3292 let name_lc = match self.peek().clone() {
3296 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
3297 Token::Default => alloc::string::String::from("default"),
3298 _ => break,
3299 };
3300 let known = matches!(
3301 name_lc.as_str(),
3302 "engine"
3303 | "default"
3304 | "charset"
3305 | "collate"
3306 | "auto_increment"
3307 | "row_format"
3308 | "comment"
3309 | "pack_keys"
3310 | "stats_persistent"
3311 | "stats_auto_recalc"
3312 | "stats_sample_pages"
3313 | "key_block_size"
3314 | "tablespace"
3315 | "min_rows"
3316 | "max_rows"
3317 | "checksum"
3318 | "delay_key_write"
3319 | "insert_method"
3320 | "data"
3321 | "index"
3322 | "encryption"
3323 | "compression"
3324 );
3325 if !known {
3326 break;
3327 }
3328 self.advance(); if name_lc == "default" {
3332 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
3333 self.advance();
3334 }
3335 }
3336 if matches!(self.peek(), Token::Eq) {
3337 self.advance();
3338 }
3339 match self.peek() {
3340 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
3341 self.advance();
3342 }
3343 _ => {}
3344 }
3345 }
3346 }
3347
3348 fn peek_table_level_pk_start(&self) -> bool {
3353 let cur = self.peek();
3354 let nxt = self.tokens.get(self.pos + 1);
3355 let nxt2 = self.tokens.get(self.pos + 2);
3356 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
3357 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
3358 let is_lparen = matches!(nxt2, Some(Token::LParen));
3359 is_primary && is_key && is_lparen
3360 }
3361
3362 fn peek_table_level_unique_start(&self) -> bool {
3366 let cur = self.peek();
3367 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
3368 if !is_unique {
3369 return false;
3370 }
3371 let n1 = self.tokens.get(self.pos + 1);
3372 if matches!(n1, Some(Token::LParen)) {
3374 return true;
3375 }
3376 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
3378 if !is_nulls {
3379 return false;
3380 }
3381 let n2 = self.tokens.get(self.pos + 2);
3382 let n3 = self.tokens.get(self.pos + 3);
3383 let n4 = self.tokens.get(self.pos + 4);
3384 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
3386 return true;
3387 }
3388 if matches!(n2, Some(Token::Not))
3390 && matches!(n3, Some(Token::Distinct))
3391 && matches!(n4, Some(Token::LParen))
3392 {
3393 return true;
3394 }
3395 false
3396 }
3397
3398 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3399 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
3402 Ok(crate::ast::TableConstraint::PrimaryKey {
3403 name: None,
3404 columns,
3405 })
3406 }
3407
3408 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3409 self.advance(); let mut nulls_not_distinct = false;
3414 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
3415 let n1 = self.tokens.get(self.pos + 1);
3416 let n2 = self.tokens.get(self.pos + 2);
3417 let is_not = matches!(n1, Some(Token::Not));
3418 let is_distinct = matches!(n2, Some(Token::Distinct));
3419 if is_not && is_distinct {
3420 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
3424 } else if matches!(n1, Some(Token::Distinct)) {
3425 self.advance(); self.advance(); }
3428 }
3429 let columns = self.parse_paren_ident_list("UNIQUE")?;
3430 Ok(crate::ast::TableConstraint::Unique {
3431 name: None,
3432 columns,
3433 nulls_not_distinct,
3434 })
3435 }
3436
3437 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
3441 self.advance(); if !matches!(self.peek(), Token::LParen) {
3443 return Err(self.err(alloc::format!(
3444 "expected '(' after CHECK, got {:?}",
3445 self.peek()
3446 )));
3447 }
3448 self.advance();
3449 let expr = self.parse_expr(0)?;
3450 if !matches!(self.peek(), Token::RParen) {
3451 return Err(self.err(alloc::format!(
3452 "expected ')' to close CHECK predicate, got {:?}",
3453 self.peek()
3454 )));
3455 }
3456 self.advance();
3457 Ok(crate::ast::TableConstraint::Check { name: None, expr })
3458 }
3459
3460 fn peek_table_level_check_start(&self) -> bool {
3462 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
3463 }
3464
3465 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
3466 if !matches!(self.peek(), Token::LParen) {
3467 return Err(self.err(alloc::format!(
3468 "expected '(' after {ctx}, got {:?}",
3469 self.peek()
3470 )));
3471 }
3472 self.advance();
3473 let mut out = Vec::new();
3474 loop {
3475 out.push(self.expect_ident_like()?);
3476 match self.peek() {
3477 Token::Comma => {
3478 self.advance();
3479 }
3480 Token::RParen => {
3481 self.advance();
3482 break;
3483 }
3484 other => {
3485 return Err(self.err(alloc::format!(
3486 "expected ',' or ')' in {ctx} list, got {other:?}"
3487 )));
3488 }
3489 }
3490 }
3491 if out.is_empty() {
3492 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
3493 }
3494 Ok(out)
3495 }
3496
3497 fn peek_constraint_or_fk_start(&self) -> bool {
3502 let is_constraint_kw = matches!(
3503 self.peek(),
3504 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3505 );
3506 let is_foreign_kw = matches!(
3507 self.peek(),
3508 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
3509 );
3510 is_constraint_kw || is_foreign_kw
3511 }
3512
3513 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
3517 let mut name: Option<String> = None;
3518 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
3519 self.advance();
3520 name = Some(self.expect_ident_like()?);
3521 }
3522 match self.advance() {
3524 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
3525 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
3526 }
3527 match self.advance() {
3529 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
3530 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
3531 }
3532 if !matches!(self.peek(), Token::LParen) {
3534 return Err(self.err(format!(
3535 "expected '(' after FOREIGN KEY, got {:?}",
3536 self.peek()
3537 )));
3538 }
3539 self.advance();
3540 let mut columns = Vec::new();
3541 loop {
3542 columns.push(self.expect_ident_like()?);
3543 match self.peek() {
3544 Token::Comma => {
3545 self.advance();
3546 }
3547 Token::RParen => {
3548 self.advance();
3549 break;
3550 }
3551 other => {
3552 return Err(self.err(format!(
3553 "expected ',' or ')' in FK column list, got {other:?}"
3554 )));
3555 }
3556 }
3557 }
3558 if columns.is_empty() {
3559 return Err(self.err("FOREIGN KEY requires at least one column".into()));
3560 }
3561 let (parent_table, parent_columns, on_delete, on_update) =
3562 self.parse_references_tail(columns.len())?;
3563 Ok(ForeignKeyConstraint {
3564 name,
3565 columns,
3566 parent_table,
3567 parent_columns,
3568 on_delete,
3569 on_update,
3570 })
3571 }
3572
3573 fn parse_references_tail(
3578 &mut self,
3579 expected_arity: usize,
3580 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
3581 match self.advance() {
3582 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
3583 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
3584 }
3585 let parent_table = self.expect_ident_like()?;
3586 let mut parent_columns: Vec<String> = Vec::new();
3587 if matches!(self.peek(), Token::LParen) {
3588 self.advance();
3589 loop {
3590 parent_columns.push(self.expect_ident_like()?);
3591 match self.peek() {
3592 Token::Comma => {
3593 self.advance();
3594 }
3595 Token::RParen => {
3596 self.advance();
3597 break;
3598 }
3599 other => {
3600 return Err(self.err(format!(
3601 "expected ',' or ')' in REFERENCES column list, got {other:?}"
3602 )));
3603 }
3604 }
3605 }
3606 }
3607 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
3608 return Err(self.err(format!(
3609 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
3610 expected_arity,
3611 parent_columns.len()
3612 )));
3613 }
3614 loop {
3620 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
3621 return Err(self.err(
3622 "DEFERRABLE constraints are not supported (SPG is single-writer; \
3623 constraints are always evaluated immediately at commit)"
3624 .into(),
3625 ));
3626 }
3627 if matches!(self.peek(), Token::Not) {
3628 let look = self.tokens.get(self.pos + 1);
3629 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
3630 self.advance();
3633 self.advance();
3634 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
3636 {
3637 self.advance();
3638 match self.advance() {
3639 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
3640 other => {
3641 return Err(self.err(format!(
3642 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
3643 got {other:?}"
3644 )));
3645 }
3646 }
3647 }
3648 continue;
3649 }
3650 break;
3651 }
3652 break;
3653 }
3654 let mut on_delete = FkAction::Restrict;
3657 let mut on_update = FkAction::Restrict;
3658 let mut seen_on_delete = false;
3659 let mut seen_on_update = false;
3660 loop {
3661 if !matches!(self.peek(), Token::On) {
3662 break;
3663 }
3664 self.advance();
3665 let which = self.advance();
3666 let action = self.parse_fk_action()?;
3667 match which {
3668 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
3669 if seen_on_delete {
3670 return Err(self.err("ON DELETE specified twice".into()));
3671 }
3672 seen_on_delete = true;
3673 on_delete = action;
3674 }
3675 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
3676 if seen_on_update {
3677 return Err(self.err("ON UPDATE specified twice".into()));
3678 }
3679 seen_on_update = true;
3680 on_update = action;
3681 }
3682 other => {
3683 return Err(
3684 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
3685 );
3686 }
3687 }
3688 }
3689 Ok((parent_table, parent_columns, on_delete, on_update))
3690 }
3691
3692 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
3695 match self.advance() {
3696 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
3697 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
3698 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
3699 Token::Null => Ok(FkAction::SetNull),
3700 Token::Default => Ok(FkAction::SetDefault),
3701 other => Err(self.err(format!(
3702 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
3703 ))),
3704 },
3705 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
3706 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
3707 other => Err(self.err(format!(
3708 "expected ACTION after NO in FK action, got {other:?}"
3709 ))),
3710 },
3711 other => Err(self.err(format!(
3712 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
3713 ))),
3714 }
3715 }
3716
3717 fn consume_if_not_exists(&mut self) -> bool {
3720 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3724 if !looks_like_if {
3725 return false;
3726 }
3727 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
3730 return false;
3731 }
3732 if !matches!(
3733 self.tokens.get(self.pos + 2),
3734 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3735 ) {
3736 return false;
3737 }
3738 self.advance(); self.advance(); self.advance(); true
3742 }
3743
3744 fn consume_if_exists(&mut self) -> bool {
3748 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
3749 if !looks_like_if {
3750 return false;
3751 }
3752 if !matches!(
3753 self.tokens.get(self.pos + 1),
3754 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
3755 ) {
3756 return false;
3757 }
3758 self.advance(); self.advance(); true
3761 }
3762
3763 fn consume_optional_index_column_qualifiers(&mut self) {
3769 loop {
3770 match self.peek() {
3771 Token::Asc | Token::Desc => {
3772 self.advance();
3773 }
3774 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
3775 let look = self.tokens.get(self.pos + 1);
3776 if matches!(
3777 look,
3778 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
3779 || k.eq_ignore_ascii_case("last")
3780 ) {
3781 self.advance();
3782 self.advance();
3783 } else {
3784 break;
3785 }
3786 }
3787 _ => break,
3788 }
3789 }
3790 }
3791
3792 fn parse_create_index_stmt_after_create(
3793 &mut self,
3794 is_unique: bool,
3795 ) -> Result<Statement, ParseError> {
3796 debug_assert!(matches!(self.peek(), Token::Index));
3798 self.advance();
3799 let if_not_exists = self.consume_if_not_exists();
3800 let name = self.expect_ident_like()?;
3801 if !matches!(self.peek(), Token::On) {
3802 return Err(self.err(format!(
3803 "expected ON after CREATE INDEX <name>, got {:?}",
3804 self.peek()
3805 )));
3806 }
3807 self.advance();
3808 let table = self.expect_ident_like()?;
3809 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3814 self.advance();
3815 let m = self.expect_ident_like()?;
3816 match m.to_ascii_lowercase().as_str() {
3817 "hnsw" => IndexMethod::Hnsw,
3818 "btree" => IndexMethod::BTree,
3819 "brin" => IndexMethod::Brin,
3820 "gin" => IndexMethod::Gin,
3825 "gist" | "spgist" | "hash" => IndexMethod::BTree,
3834 "ivfflat" => IndexMethod::Hnsw,
3842 other => {
3843 return Err(self.err(alloc::format!(
3844 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
3845 )));
3846 }
3847 }
3848 } else {
3849 IndexMethod::BTree
3850 };
3851 if !matches!(self.peek(), Token::LParen) {
3852 return Err(self.err(format!(
3853 "expected '(' before indexed column, got {:?}",
3854 self.peek()
3855 )));
3856 }
3857 self.advance();
3858 let mut opclass: Option<String> = None;
3867 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
3868 Token::Ident(s) | Token::QuotedIdent(s)
3875 if matches!(
3876 self.tokens.get(self.pos + 1),
3877 Some(Token::RParen | Token::Comma)
3878 ) =>
3879 {
3880 self.advance();
3881 (s, None)
3882 }
3883 Token::Ident(s) | Token::QuotedIdent(s)
3893 if matches!(
3894 self.tokens.get(self.pos + 1),
3895 Some(Token::Ident(op) | Token::QuotedIdent(op))
3896 if is_vector_opclass_name(op)
3897 ) =>
3898 {
3899 self.advance(); let op_tok = self.advance();
3903 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
3904 opclass = Some(op.to_ascii_lowercase());
3905 }
3906 (s, None)
3907 }
3908 Token::Ident(_) | Token::QuotedIdent(_) => {
3909 let key_expr = self.parse_expr(0)?;
3910 let primary = extract_first_column(&key_expr).ok_or_else(|| {
3911 self.err("expression index key must reference at least one column".into())
3912 })?;
3913 (primary, Some(key_expr))
3914 }
3915 other => {
3916 return Err(self.err(format!(
3917 "expected column ident or expression, got {other:?}"
3918 )));
3919 }
3920 };
3921 let mut extra_columns: Vec<String> = Vec::new();
3930 self.consume_optional_index_column_qualifiers();
3932 while matches!(self.peek(), Token::Comma) {
3933 self.advance();
3934 let extra = self.expect_ident_like()?;
3935 self.consume_optional_index_column_qualifiers();
3936 extra_columns.push(extra);
3937 }
3938 if !matches!(self.peek(), Token::RParen) {
3939 return Err(self.err(format!(
3940 "expected ')' after indexed column / expression, got {:?}",
3941 self.peek()
3942 )));
3943 }
3944 self.advance();
3945 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
3949 {
3950 self.advance();
3951 if !matches!(self.peek(), Token::LParen) {
3952 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
3953 }
3954 self.advance();
3955 let mut cols = Vec::new();
3956 loop {
3957 cols.push(self.expect_ident_like()?);
3958 match self.peek() {
3959 Token::Comma => {
3960 self.advance();
3961 }
3962 Token::RParen => {
3963 self.advance();
3964 break;
3965 }
3966 other => {
3967 return Err(self.err(format!(
3968 "expected ',' or ')' in INCLUDE list, got {other:?}"
3969 )));
3970 }
3971 }
3972 }
3973 cols
3974 } else {
3975 Vec::new()
3976 };
3977 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3983 self.advance();
3984 if !matches!(self.peek(), Token::LParen) {
3985 return Err(self.err(format!(
3986 "expected '(' after WITH in CREATE INDEX, got {:?}",
3987 self.peek()
3988 )));
3989 }
3990 self.advance();
3991 loop {
3992 if matches!(self.peek(), Token::RParen) {
3993 self.advance();
3994 break;
3995 }
3996 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
3999 self.advance();
4000 let _ = self.advance(); }
4002 match self.peek() {
4003 Token::Comma => {
4004 self.advance();
4005 }
4006 Token::RParen => {
4007 self.advance();
4008 break;
4009 }
4010 other => {
4011 return Err(self.err(format!(
4012 "expected ',' or ')' in WITH (…) clause, got {other:?}"
4013 )));
4014 }
4015 }
4016 }
4017 }
4018 let partial_predicate = if matches!(self.peek(), Token::Where) {
4020 self.advance();
4021 Some(self.parse_expr(0)?)
4022 } else {
4023 None
4024 };
4025 if is_unique && !matches!(method, IndexMethod::BTree) {
4030 return Err(self.err(alloc::format!(
4031 "UNIQUE is only supported on BTree indexes, got USING {:?}",
4032 method
4033 )));
4034 }
4035 Ok(Statement::CreateIndex(CreateIndexStatement {
4036 name,
4037 table,
4038 column,
4039 method,
4040 if_not_exists,
4041 included_columns,
4042 partial_predicate,
4043 extra_columns: extra_columns.clone(),
4044 expression,
4045 is_unique,
4046 opclass,
4047 }))
4048 }
4049
4050 fn parse_column_def_with_fk(
4055 &mut self,
4056 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
4057 let col = self.parse_column_def()?;
4058 let inline_references = matches!(
4060 self.peek(),
4061 Token::Ident(s) if s.eq_ignore_ascii_case("references")
4062 );
4063 if !inline_references {
4064 return Ok((col, None));
4065 }
4066 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
4067 let fk = ForeignKeyConstraint {
4068 name: None,
4069 columns: vec![col.name.clone()],
4070 parent_table,
4071 parent_columns,
4072 on_delete,
4073 on_update,
4074 };
4075 Ok((col, Some(fk)))
4076 }
4077
4078 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
4086 let (ty, _, _) = self.parse_type_with_implied_flags()?;
4087 Ok(ty)
4088 }
4089
4090 fn parse_type_with_implied_flags(
4091 &mut self,
4092 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
4093 let ty_ident = match self.advance() {
4094 Token::Ident(s) => s,
4095 other => {
4096 return Err(ParseError {
4097 message: format!("expected column type, got {other:?}"),
4098 token_pos: self.pos.saturating_sub(1),
4099 });
4100 }
4101 };
4102 let mut implied_auto_increment = false;
4103 let mut implied_not_null = false;
4104 let mut ty = match ty_ident.as_str() {
4105 "smallserial" | "serial2" => {
4107 implied_auto_increment = true;
4108 implied_not_null = true;
4109 ColumnTypeName::SmallInt
4110 }
4111 "serial" | "serial4" => {
4112 implied_auto_increment = true;
4113 implied_not_null = true;
4114 ColumnTypeName::Int
4115 }
4116 "bigserial" | "serial8" => {
4117 implied_auto_increment = true;
4118 implied_not_null = true;
4119 ColumnTypeName::BigInt
4120 }
4121 "smallint" | "tinyint" => {
4127 self.consume_optional_paren_size();
4132 ColumnTypeName::SmallInt
4133 }
4134 "int" | "integer" | "mediumint" => {
4135 self.consume_optional_paren_size();
4136 ColumnTypeName::Int
4137 }
4138 "bigint" => {
4139 self.consume_optional_paren_size();
4140 ColumnTypeName::BigInt
4141 }
4142 "float" | "double" | "real" => {
4147 if ty_ident.eq_ignore_ascii_case("double")
4148 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
4149 {
4150 self.advance();
4151 }
4152 ColumnTypeName::Float
4153 }
4154 "float4" | "float8" => ColumnTypeName::Float,
4156 "text" => ColumnTypeName::Text,
4157 "bool" | "boolean" => ColumnTypeName::Bool,
4158 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
4159 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
4160 "vector" => {
4161 let dim = self.parse_paren_size("VECTOR")?;
4162 let encoding = self.parse_optional_vector_encoding()?;
4163 ColumnTypeName::Vector { dim, encoding }
4164 }
4165 "numeric" => {
4166 let (precision, scale) = self.parse_optional_numeric_params()?;
4167 ColumnTypeName::Numeric(precision, scale)
4168 }
4169 "date" => ColumnTypeName::Date,
4170 "timestamp" | "datetime" => {
4173 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
4179 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
4180 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
4181 {
4182 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
4186 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
4187 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
4188 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
4189 {
4190 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
4194 } else {
4195 self.consume_optional_paren_size();
4199 ColumnTypeName::Timestamp
4200 }
4201 }
4202 "timestamptz" => ColumnTypeName::Timestamptz,
4206 "json" => ColumnTypeName::Json,
4211 "jsonb" => ColumnTypeName::Jsonb,
4212 "bytea" | "bytes" => ColumnTypeName::Bytes,
4218 "tsvector" => ColumnTypeName::TsVector,
4223 "tsquery" => ColumnTypeName::TsQuery,
4224 other => {
4225 return Err(ParseError {
4226 message: format!("unsupported column type {other:?}"),
4227 token_pos: self.pos.saturating_sub(1),
4228 });
4229 }
4230 };
4231 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
4236 self.advance();
4237 }
4238 loop {
4244 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
4245 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
4246 {
4247 self.advance(); self.advance(); if matches!(
4250 self.peek(),
4251 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
4252 ) {
4253 self.advance();
4254 }
4255 continue;
4256 }
4257 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
4258 self.advance(); if matches!(
4260 self.peek(),
4261 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
4262 ) {
4263 self.advance();
4264 }
4265 continue;
4266 }
4267 break;
4268 }
4269 if matches!(self.peek(), Token::LBracket) {
4274 self.advance();
4275 if !matches!(self.peek(), Token::RBracket) {
4276 return Err(self.err(alloc::format!(
4277 "TEXT[] takes no dimension; got {:?}",
4278 self.peek()
4279 )));
4280 }
4281 self.advance();
4282 ty = match ty {
4286 ColumnTypeName::Text => ColumnTypeName::TextArray,
4287 ColumnTypeName::Int => ColumnTypeName::IntArray,
4288 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
4289 other => {
4290 return Err(self.err(alloc::format!(
4291 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
4292 )));
4293 }
4294 };
4295 }
4296 Ok((ty, implied_auto_increment, implied_not_null))
4297 }
4298
4299 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
4300 let name = self.expect_ident_like()?;
4301 let (ty, implied_auto_increment, implied_not_null) =
4302 self.parse_type_with_implied_flags()?;
4303 let mut default: Option<Expr> = None;
4307 let mut nullable = !implied_not_null;
4308 let mut nullability_seen = implied_not_null;
4309 let mut auto_increment = implied_auto_increment;
4310 let mut is_primary_key = false;
4311 let mut is_unique = false;
4312 let mut check: Option<Expr> = None;
4313 loop {
4314 if matches!(self.peek(), Token::Default) {
4315 if default.is_some() {
4316 return Err(self.err("DEFAULT specified twice".into()));
4317 }
4318 self.advance();
4319 default = Some(self.parse_expr(0)?);
4320 continue;
4321 }
4322 if matches!(self.peek(), Token::Not) {
4323 if nullability_seen {
4324 return Err(self.err("NOT NULL specified twice".into()));
4325 }
4326 self.advance();
4327 if !matches!(self.peek(), Token::Null) {
4328 return Err(self.err(format!(
4329 "expected NULL after NOT in column def, got {:?}",
4330 self.peek()
4331 )));
4332 }
4333 self.advance();
4334 nullable = false;
4335 nullability_seen = true;
4336 continue;
4337 }
4338 if matches!(self.peek(), Token::Null) {
4344 if nullability_seen && !nullable {
4345 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
4346 }
4347 self.advance();
4348 nullable = true;
4349 nullability_seen = true;
4350 continue;
4351 }
4352 if let Token::Ident(s) = self.peek()
4355 && (s.eq_ignore_ascii_case("auto_increment")
4356 || s.eq_ignore_ascii_case("autoincrement"))
4357 {
4358 if auto_increment {
4359 return Err(self.err("AUTO_INCREMENT specified twice".into()));
4360 }
4361 self.advance();
4362 auto_increment = true;
4363 continue;
4364 }
4365 if let Token::Ident(s) = self.peek()
4370 && s.eq_ignore_ascii_case("primary")
4371 {
4372 if is_primary_key {
4373 return Err(self.err("PRIMARY KEY specified twice".into()));
4374 }
4375 let next = self.tokens.get(self.pos + 1);
4377 let next_is_key = matches!(
4378 next,
4379 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
4380 );
4381 if !next_is_key {
4382 return Err(self.err(format!(
4383 "expected KEY after PRIMARY in column def, got {:?}",
4384 next
4385 )));
4386 }
4387 self.advance(); self.advance(); is_primary_key = true;
4390 if nullability_seen && nullable {
4391 return Err(self.err(
4392 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
4393 ));
4394 }
4395 nullable = false;
4396 nullability_seen = true;
4397 continue;
4398 }
4399 if let Token::Ident(s) = self.peek()
4403 && s.eq_ignore_ascii_case("unique")
4404 {
4405 if is_unique {
4406 return Err(self.err("UNIQUE specified twice".into()));
4407 }
4408 self.advance();
4409 is_unique = true;
4410 continue;
4411 }
4412 if let Token::Ident(s) = self.peek()
4417 && s.eq_ignore_ascii_case("check")
4418 {
4419 self.advance();
4420 if !matches!(self.peek(), Token::LParen) {
4421 return Err(self.err(alloc::format!(
4422 "expected '(' after CHECK in column def, got {:?}",
4423 self.peek()
4424 )));
4425 }
4426 self.advance();
4427 let pred = self.parse_expr(0)?;
4428 if !matches!(self.peek(), Token::RParen) {
4429 return Err(self.err(alloc::format!(
4430 "expected ')' to close CHECK predicate, got {:?}",
4431 self.peek()
4432 )));
4433 }
4434 self.advance();
4435 check = Some(match check.take() {
4436 Some(prev) => Expr::Binary {
4437 op: BinOp::And,
4438 lhs: Box::new(prev),
4439 rhs: Box::new(pred),
4440 },
4441 None => pred,
4442 });
4443 continue;
4444 }
4445 break;
4446 }
4447 Ok(ColumnDef {
4448 name,
4449 ty,
4450 nullable,
4451 default,
4452 auto_increment,
4453 is_primary_key,
4454 is_unique,
4455 check,
4456 })
4457 }
4458
4459 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
4463 if !matches!(self.peek(), Token::LParen) {
4464 return Ok((0, 0));
4468 }
4469 self.advance();
4470 let precision = match self.advance() {
4471 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
4472 other => {
4473 return Err(ParseError {
4474 message: format!(
4475 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
4476 ),
4477 token_pos: self.pos.saturating_sub(1),
4478 });
4479 }
4480 };
4481 let scale = if matches!(self.peek(), Token::Comma) {
4482 self.advance();
4483 match self.advance() {
4484 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
4485 u8::try_from(n).expect("range-checked")
4486 }
4487 other => {
4488 return Err(ParseError {
4489 message: format!(
4490 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
4491 ),
4492 token_pos: self.pos.saturating_sub(1),
4493 });
4494 }
4495 }
4496 } else {
4497 0
4498 };
4499 if !matches!(self.peek(), Token::RParen) {
4500 return Err(self.err(format!(
4501 "expected ')' to close NUMERIC params, got {:?}",
4502 self.peek()
4503 )));
4504 }
4505 self.advance();
4506 Ok((precision, scale))
4507 }
4508
4509 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
4517 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4518 return Ok(VecEncoding::F32);
4519 }
4520 let n1 = self.tokens.get(self.pos + 1);
4526 let next_is_encoding = matches!(
4527 n1,
4528 Some(Token::Ident(s))
4529 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
4530 );
4531 if !next_is_encoding {
4532 return Ok(VecEncoding::F32);
4533 }
4534 self.advance();
4535 let enc_ident = match self.advance() {
4536 Token::Ident(s) => s,
4537 other => {
4538 return Err(self.err(format!(
4539 "expected vector encoding after USING, got {other:?}"
4540 )));
4541 }
4542 };
4543 match enc_ident.to_ascii_lowercase().as_str() {
4544 "sq8" => Ok(VecEncoding::Sq8),
4545 "half" => Ok(VecEncoding::F16),
4548 other => Err(self.err(format!(
4549 "unknown vector encoding {other:?}; supported: SQ8, HALF"
4550 ))),
4551 }
4552 }
4553
4554 fn consume_optional_paren_size(&mut self) {
4558 if !matches!(self.peek(), Token::LParen) {
4559 return;
4560 }
4561 self.advance();
4562 let mut depth = 1usize;
4564 while depth > 0 {
4565 match self.peek() {
4566 Token::LParen => depth += 1,
4567 Token::RParen => depth -= 1,
4568 Token::Eof => return,
4569 _ => {}
4570 }
4571 self.advance();
4572 }
4573 }
4574
4575 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
4576 if !matches!(self.peek(), Token::LParen) {
4577 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
4578 }
4579 self.advance();
4580 let n = match self.advance() {
4581 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
4582 message: format!("{label} size too large: {n}"),
4583 token_pos: self.pos.saturating_sub(1),
4584 })?,
4585 other => {
4586 return Err(ParseError {
4587 message: format!("expected positive integer {label} size, got {other:?}"),
4588 token_pos: self.pos.saturating_sub(1),
4589 });
4590 }
4591 };
4592 if !matches!(self.peek(), Token::RParen) {
4593 return Err(self.err(format!(
4594 "expected ')' after {label} size, got {:?}",
4595 self.peek()
4596 )));
4597 }
4598 self.advance();
4599 Ok(n)
4600 }
4601
4602 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
4603 debug_assert!(matches!(self.peek(), Token::Insert));
4604 self.advance();
4605 if !matches!(self.peek(), Token::Into) {
4606 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
4607 }
4608 self.advance();
4609 let table = self.expect_ident_like()?;
4610 let columns = if matches!(self.peek(), Token::LParen) {
4612 self.advance();
4613 let mut names = Vec::new();
4614 loop {
4615 names.push(self.expect_ident_like()?);
4616 match self.peek() {
4617 Token::Comma => {
4618 self.advance();
4619 }
4620 Token::RParen => {
4621 self.advance();
4622 break;
4623 }
4624 other => {
4625 return Err(self.err(format!(
4626 "expected ',' or ')' in INSERT column list, got {other:?}"
4627 )));
4628 }
4629 }
4630 }
4631 Some(names)
4632 } else {
4633 None
4634 };
4635 if matches!(self.peek(), Token::Select) {
4638 let select_stmt = match self.parse_select_stmt()? {
4639 Statement::Select(s) => s,
4640 other => {
4641 return Err(self.err(alloc::format!(
4642 "expected SELECT after INSERT INTO ... target, got {other:?}"
4643 )));
4644 }
4645 };
4646 let on_conflict = self.parse_optional_on_conflict()?;
4647 let returning = self.parse_optional_returning()?;
4648 return Ok(Statement::Insert(InsertStatement {
4649 table,
4650 columns,
4651 rows: Vec::new(),
4652 select_source: Some(Box::new(select_stmt)),
4653 on_conflict,
4654 returning,
4655 }));
4656 }
4657 if !matches!(self.peek(), Token::Values) {
4658 return Err(self.err(format!(
4659 "expected VALUES or SELECT after table name, got {:?}",
4660 self.peek()
4661 )));
4662 }
4663 self.advance();
4664 if !matches!(self.peek(), Token::LParen) {
4665 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
4666 }
4667 let mut rows = Vec::new();
4668 loop {
4669 if !matches!(self.peek(), Token::LParen) {
4671 return Err(self.err(format!(
4672 "expected '(' for next VALUES tuple, got {:?}",
4673 self.peek()
4674 )));
4675 }
4676 self.advance();
4677 let mut tuple = Vec::new();
4678 loop {
4679 tuple.push(self.parse_expr(0)?);
4680 match self.peek() {
4681 Token::Comma => {
4682 self.advance();
4683 }
4684 Token::RParen => {
4685 self.advance();
4686 break;
4687 }
4688 other => {
4689 return Err(self.err(format!(
4690 "expected ',' or ')' in VALUES tuple, got {other:?}"
4691 )));
4692 }
4693 }
4694 }
4695 if tuple.is_empty() {
4696 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
4697 }
4698 rows.push(tuple);
4699 if matches!(self.peek(), Token::Comma) {
4701 self.advance();
4702 } else {
4703 break;
4704 }
4705 }
4706 let on_conflict = self.parse_optional_on_conflict()?;
4707 let returning = self.parse_optional_returning()?;
4708 Ok(Statement::Insert(InsertStatement {
4709 table,
4710 columns,
4711 rows,
4712 select_source: None,
4713 on_conflict,
4714 returning,
4715 }))
4716 }
4717
4718 fn parse_optional_on_conflict(
4723 &mut self,
4724 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
4725 if !matches!(self.peek(), Token::On) {
4726 return Ok(None);
4727 }
4728 let next_is_conflict = matches!(
4731 self.tokens.get(self.pos + 1),
4732 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
4733 );
4734 if !next_is_conflict {
4735 return Ok(None);
4736 }
4737 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
4741 if matches!(self.peek(), Token::LParen) {
4742 self.advance();
4743 loop {
4744 target_columns.push(self.expect_ident_like()?);
4745 match self.peek() {
4746 Token::Comma => {
4747 self.advance();
4748 }
4749 Token::RParen => {
4750 self.advance();
4751 break;
4752 }
4753 other => {
4754 return Err(self.err(alloc::format!(
4755 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
4756 )));
4757 }
4758 }
4759 }
4760 }
4761 match self.advance() {
4763 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
4764 other => {
4765 return Err(self.err(alloc::format!(
4766 "expected DO after ON CONFLICT [(…)], got {other:?}"
4767 )));
4768 }
4769 }
4770 let action = match self.advance() {
4772 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
4773 crate::ast::OnConflictAction::Nothing
4774 }
4775 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
4776 self.parse_on_conflict_update_action()?
4777 }
4778 other => {
4779 return Err(self.err(alloc::format!(
4780 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
4781 )));
4782 }
4783 };
4784 Ok(Some(crate::ast::OnConflictClause {
4785 target_columns,
4786 action,
4787 }))
4788 }
4789
4790 fn parse_on_conflict_update_action(
4794 &mut self,
4795 ) -> Result<crate::ast::OnConflictAction, ParseError> {
4796 match self.advance() {
4798 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
4799 other => {
4800 return Err(self.err(alloc::format!(
4801 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
4802 )));
4803 }
4804 }
4805 let mut assignments: Vec<(String, Expr)> = Vec::new();
4806 loop {
4807 let col = self.expect_ident_like()?;
4808 if !matches!(self.peek(), Token::Eq) {
4809 return Err(self.err(alloc::format!(
4810 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
4811 self.peek()
4812 )));
4813 }
4814 self.advance();
4815 let value = self.parse_expr(0)?;
4816 assignments.push((col, value));
4817 if matches!(self.peek(), Token::Comma) {
4818 self.advance();
4819 continue;
4820 }
4821 break;
4822 }
4823 let where_ = if matches!(self.peek(), Token::Where) {
4824 self.advance();
4825 Some(self.parse_expr(0)?)
4826 } else {
4827 None
4828 };
4829 Ok(crate::ast::OnConflictAction::Update {
4830 assignments,
4831 where_,
4832 })
4833 }
4834
4835 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
4836 let mut items = Vec::new();
4837 loop {
4838 items.push(self.parse_select_item()?);
4839 if matches!(self.peek(), Token::Comma) {
4840 self.advance();
4841 } else {
4842 break;
4843 }
4844 }
4845 Ok(items)
4846 }
4847
4848 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
4849 if matches!(self.peek(), Token::Star) {
4850 self.advance();
4851 return Ok(SelectItem::Wildcard);
4852 }
4853 let expr = self.parse_expr(0)?;
4854 let alias = self.parse_optional_alias();
4855 Ok(SelectItem::Expr { expr, alias })
4856 }
4857
4858 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
4859 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
4863 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4864 {
4865 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
4868 if !matches!(self.peek(), Token::RParen) {
4869 return Err(self.err(alloc::format!(
4870 "expected ')' after unnest() argument, got {:?}",
4871 self.peek()
4872 )));
4873 }
4874 self.advance();
4875 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
4876 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
4877 return Ok(TableRef {
4878 name,
4879 alias: alias_ident,
4880 as_of_segment: None,
4881 unnest_expr: Some(Box::new(expr)),
4882 unnest_column_aliases,
4883 });
4884 }
4885 let name = if let Some(synth) = self.try_peek_meta_qualified() {
4894 synth
4895 } else {
4896 self.expect_ident_like()?
4897 };
4898 let as_of_segment = if matches!(self.peek(), Token::As)
4904 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
4905 {
4906 self.advance(); self.advance(); let kw = match self.peek().clone() {
4909 Token::Ident(s) | Token::QuotedIdent(s) => s,
4910 other => {
4911 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
4912 }
4913 };
4914 if !kw.eq_ignore_ascii_case("segment") {
4915 return Err(self.err(format!(
4916 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
4917 )));
4918 }
4919 self.advance();
4920 let id = match self.advance() {
4923 Token::String(s) => s
4924 .parse::<u32>()
4925 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4926 Token::Integer(n) => u32::try_from(n)
4927 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
4928 other => {
4929 return Err(self.err(format!(
4930 "expected segment id literal after AS OF SEGMENT, got {other:?}"
4931 )));
4932 }
4933 };
4934 Some(id)
4935 } else {
4936 None
4937 };
4938 let alias = self.parse_optional_alias();
4939 Ok(TableRef {
4940 name,
4941 alias,
4942 as_of_segment,
4943 unnest_expr: None,
4944 unnest_column_aliases: Vec::new(),
4945 })
4946 }
4947
4948 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
4954 let alias = self.parse_optional_alias();
4955 if alias.is_none() {
4956 return (None, Vec::new());
4957 }
4958 let mut cols: Vec<String> = Vec::new();
4959 if matches!(self.peek(), Token::LParen) {
4960 self.advance();
4961 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
4962 self.advance();
4963 cols.push(s);
4964 if matches!(self.peek(), Token::Comma) {
4965 self.advance();
4966 continue;
4967 }
4968 break;
4969 }
4970 if matches!(self.peek(), Token::RParen) {
4971 self.advance();
4972 }
4973 }
4974 (alias, cols)
4975 }
4976
4977 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
4982 let primary = self.parse_table_ref()?;
4983 let mut joins = Vec::new();
4984 loop {
4985 if matches!(self.peek(), Token::Comma) {
4987 self.advance();
4988 let table = self.parse_table_ref()?;
4989 joins.push(FromJoin {
4990 kind: JoinKind::Cross,
4991 table,
4992 on: None,
4993 });
4994 continue;
4995 }
4996 let kind =
4999 match self.peek() {
5000 Token::Inner => {
5001 self.advance();
5002 if !matches!(self.peek(), Token::Join) {
5003 return Err(self
5004 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
5005 }
5006 self.advance();
5007 JoinKind::Inner
5008 }
5009 Token::Left => {
5010 self.advance();
5011 if matches!(self.peek(), Token::Outer) {
5012 self.advance();
5013 }
5014 if !matches!(self.peek(), Token::Join) {
5015 return Err(self.err(format!(
5016 "expected JOIN after LEFT [OUTER], got {:?}",
5017 self.peek()
5018 )));
5019 }
5020 self.advance();
5021 JoinKind::Left
5022 }
5023 Token::Cross => {
5024 self.advance();
5025 if !matches!(self.peek(), Token::Join) {
5026 return Err(self
5027 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
5028 }
5029 self.advance();
5030 JoinKind::Cross
5031 }
5032 Token::Join => {
5033 self.advance();
5034 JoinKind::Inner
5035 }
5036 _ => break,
5037 };
5038 let table = self.parse_table_ref()?;
5039 let on = if matches!(self.peek(), Token::On) {
5040 self.advance();
5041 Some(self.parse_expr(0)?)
5042 } else if kind == JoinKind::Cross {
5043 None
5044 } else {
5045 return Err(self.err(format!(
5046 "expected ON after {:?} JOIN, got {:?}",
5047 kind,
5048 self.peek()
5049 )));
5050 };
5051 joins.push(FromJoin { kind, table, on });
5052 }
5053 Ok(FromClause { primary, joins })
5054 }
5055
5056 fn parse_optional_alias(&mut self) -> Option<String> {
5061 if matches!(self.peek(), Token::As) {
5062 self.advance();
5063 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
5068 return self.expect_ident_like().ok();
5069 }
5070 return None;
5071 }
5072 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
5073 return self.expect_ident_like().ok();
5074 }
5075 None
5076 }
5077
5078 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
5080 let mut lhs = self.parse_unary()?;
5081 while let Some((op, prec)) = binop_from(self.peek()) {
5082 if prec < min_prec {
5083 break;
5084 }
5085 self.advance();
5086 let any_kind = match self.peek() {
5091 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
5092 Some(false)
5093 }
5094 Token::Ident(s) | Token::QuotedIdent(s)
5095 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
5096 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
5097 {
5098 Some(s.eq_ignore_ascii_case("any"))
5099 }
5100 _ => None,
5101 };
5102 if let Some(is_any) = any_kind {
5103 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
5106 if !matches!(self.peek(), Token::RParen) {
5107 return Err(self.err(alloc::format!(
5108 "expected ')' after ANY/ALL argument, got {:?}",
5109 self.peek()
5110 )));
5111 }
5112 self.advance();
5113 lhs = Expr::AnyAll {
5114 expr: Box::new(lhs),
5115 op,
5116 array: Box::new(arr),
5117 is_any,
5118 };
5119 continue;
5120 }
5121 let rhs = self.parse_expr(prec + 1)?;
5122 lhs = Expr::Binary {
5123 lhs: Box::new(lhs),
5124 op,
5125 rhs: Box::new(rhs),
5126 };
5127 }
5128 Ok(lhs)
5129 }
5130
5131 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
5132 match self.peek() {
5133 Token::Not => {
5134 self.advance();
5135 let e = self.parse_expr(3)?;
5138 Ok(Expr::Unary {
5139 op: UnOp::Not,
5140 expr: Box::new(e),
5141 })
5142 }
5143 Token::Minus => {
5144 self.advance();
5145 let e = self.parse_expr(8)?;
5148 Ok(Expr::Unary {
5149 op: UnOp::Neg,
5150 expr: Box::new(e),
5151 })
5152 }
5153 _ => self.parse_atom(),
5154 }
5155 }
5156
5157 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
5158 let tok_pos = self.pos;
5159 match self.advance() {
5160 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
5161 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
5162 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
5163 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
5164 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
5165 Token::Null => Ok(Expr::Literal(Literal::Null)),
5166 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
5170 Token::LParen => {
5171 if matches!(self.peek(), Token::Select) {
5175 let inner = self.parse_select_stmt()?;
5176 match self.advance() {
5177 Token::RParen => {
5178 let Statement::Select(s) = inner else {
5179 unreachable!("parse_select_stmt returns Select")
5180 };
5181 Ok(Expr::ScalarSubquery(Box::new(s)))
5182 }
5183 other => Err(ParseError {
5184 message: format!("expected ')' after scalar subquery, got {other:?}"),
5185 token_pos: self.pos.saturating_sub(1),
5186 }),
5187 }
5188 } else {
5189 let e = self.parse_expr(0)?;
5190 match self.advance() {
5191 Token::RParen => Ok(e),
5192 other => Err(ParseError {
5193 message: format!("expected ')', got {other:?}"),
5194 token_pos: self.pos.saturating_sub(1),
5195 }),
5196 }
5197 }
5198 }
5199 Token::LBracket => self.parse_vector_literal_body(),
5200 Token::Extract => self.parse_extract_atom(),
5201 Token::Interval => self.parse_interval_atom(),
5202 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
5207 self.parse_exists_atom(false)
5208 }
5209 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
5213 self.parse_case_atom()
5214 }
5215 Token::Ident(s) | Token::QuotedIdent(s)
5219 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
5220 {
5221 self.advance(); let mut items: Vec<Expr> = Vec::new();
5223 if !matches!(self.peek(), Token::RBracket) {
5224 loop {
5225 items.push(self.parse_expr(0)?);
5226 match self.peek() {
5227 Token::Comma => {
5228 self.advance();
5229 }
5230 Token::RBracket => break,
5231 other => {
5232 return Err(self.err(alloc::format!(
5233 "expected ',' or ']' in ARRAY literal, got {other:?}"
5234 )));
5235 }
5236 }
5237 }
5238 }
5239 self.advance(); Ok(Expr::Array(items))
5241 }
5242 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
5243 other => Err(ParseError {
5244 message: format!("unexpected token {other:?} in expression"),
5245 token_pos: tok_pos,
5246 }),
5247 }
5248 .and_then(|atom| self.finish_postfix_casts(atom))
5250 }
5251
5252 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
5255 loop {
5256 if matches!(self.peek(), Token::DoubleColon) {
5257 self.advance();
5258 let target = match self.advance() {
5263 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
5264 "int" | "integer" | "int4" => {
5265 if matches!(self.peek(), Token::LBracket)
5266 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5267 {
5268 self.advance();
5269 self.advance();
5270 CastTarget::IntArray
5271 } else {
5272 CastTarget::Int
5273 }
5274 }
5275 "bigint" | "int8" => {
5276 if matches!(self.peek(), Token::LBracket)
5277 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5278 {
5279 self.advance();
5280 self.advance();
5281 CastTarget::BigIntArray
5282 } else {
5283 CastTarget::BigInt
5284 }
5285 }
5286 "float" | "double" | "real" => CastTarget::Float,
5287 "text" => {
5288 if matches!(self.peek(), Token::LBracket)
5290 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
5291 {
5292 self.advance();
5293 self.advance();
5294 CastTarget::TextArray
5295 } else {
5296 CastTarget::Text
5297 }
5298 }
5299 "bool" | "boolean" => CastTarget::Bool,
5300 "vector" => CastTarget::Vector,
5301 "date" => CastTarget::Date,
5302 "timestamp" | "datetime" => CastTarget::Timestamp,
5303 "timestamptz" => CastTarget::Timestamptz,
5304 "interval" => CastTarget::Interval,
5305 "json" => CastTarget::Json,
5306 "jsonb" => CastTarget::Jsonb,
5307 "regtype" => CastTarget::RegType,
5308 "regclass" => CastTarget::RegClass,
5309 "tsvector" => CastTarget::TsVector,
5313 "tsquery" => CastTarget::TsQuery,
5314 other => {
5315 return Err(ParseError {
5316 message: format!("unsupported cast target `::{other}`"),
5317 token_pos: self.pos.saturating_sub(1),
5318 });
5319 }
5320 },
5321 Token::Interval => CastTarget::Interval,
5322 other => {
5323 return Err(ParseError {
5324 message: format!("expected type ident after `::`, got {other:?}"),
5325 token_pos: self.pos.saturating_sub(1),
5326 });
5327 }
5328 };
5329 expr = Expr::Cast {
5330 expr: Box::new(expr),
5331 target,
5332 };
5333 continue;
5334 }
5335 if matches!(self.peek(), Token::Is) {
5336 self.advance();
5337 let negated = if matches!(self.peek(), Token::Not) {
5338 self.advance();
5339 true
5340 } else {
5341 false
5342 };
5343 if matches!(self.peek(), Token::Distinct) {
5346 self.advance();
5347 if !matches!(self.peek(), Token::From) {
5348 return Err(self.err(format!(
5349 "expected FROM after IS{} DISTINCT, got {:?}",
5350 if negated { " NOT" } else { "" },
5351 self.peek()
5352 )));
5353 }
5354 self.advance();
5355 let rhs = self.parse_expr(20)?;
5359 let op = if negated {
5360 BinOp::IsNotDistinctFrom
5361 } else {
5362 BinOp::IsDistinctFrom
5363 };
5364 expr = Expr::Binary {
5365 op,
5366 lhs: Box::new(expr),
5367 rhs: Box::new(rhs),
5368 };
5369 continue;
5370 }
5371 if !matches!(self.peek(), Token::Null) {
5372 return Err(self.err(format!(
5373 "expected NULL or DISTINCT after IS{}, got {:?}",
5374 if negated { " NOT" } else { "" },
5375 self.peek()
5376 )));
5377 }
5378 self.advance();
5379 expr = Expr::IsNull {
5380 expr: Box::new(expr),
5381 negated,
5382 };
5383 continue;
5384 }
5385 let negated = if matches!(self.peek(), Token::Not) {
5389 let next = self.tokens.get(self.pos + 1);
5390 matches!(next, Some(Token::Between | Token::In | Token::Like))
5391 } else {
5392 false
5393 };
5394 if negated {
5395 self.advance();
5396 }
5397 if matches!(self.peek(), Token::Between) {
5398 expr = self.parse_between_tail(expr, negated)?;
5399 continue;
5400 }
5401 if matches!(self.peek(), Token::In) {
5402 expr = self.parse_in_tail(expr, negated)?;
5403 continue;
5404 }
5405 if matches!(self.peek(), Token::Like) {
5406 self.advance();
5407 let pattern = self.parse_expr(5)?;
5410 expr = Expr::Like {
5411 expr: Box::new(expr),
5412 pattern: Box::new(pattern),
5413 negated,
5414 };
5415 continue;
5416 }
5417 if matches!(self.peek(), Token::LBracket) {
5421 self.advance();
5422 let index = self.parse_expr(0)?;
5423 if !matches!(self.peek(), Token::RBracket) {
5424 return Err(self.err(alloc::format!(
5425 "expected ']' after array index, got {:?}",
5426 self.peek()
5427 )));
5428 }
5429 self.advance();
5430 expr = Expr::ArraySubscript {
5431 target: Box::new(expr),
5432 index: Box::new(index),
5433 };
5434 continue;
5435 }
5436 return Ok(expr);
5437 }
5438 }
5439
5440 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5444 self.advance(); let low = self.parse_expr(5)?;
5446 if !matches!(self.peek(), Token::And) {
5447 return Err(self.err(format!(
5448 "expected AND after BETWEEN low bound, got {:?}",
5449 self.peek()
5450 )));
5451 }
5452 self.advance();
5453 let high = self.parse_expr(5)?;
5454 let target = Box::new(expr);
5455 let combined = Expr::Binary {
5456 lhs: Box::new(Expr::Binary {
5457 lhs: target.clone(),
5458 op: BinOp::GtEq,
5459 rhs: Box::new(low),
5460 }),
5461 op: BinOp::And,
5462 rhs: Box::new(Expr::Binary {
5463 lhs: target,
5464 op: BinOp::LtEq,
5465 rhs: Box::new(high),
5466 }),
5467 };
5468 Ok(maybe_not(combined, negated))
5469 }
5470
5471 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
5476 let mut recursive = false;
5481 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5482 && s.eq_ignore_ascii_case("recursive")
5483 {
5484 self.advance();
5485 recursive = true;
5486 }
5487 let mut ctes = Vec::new();
5488 loop {
5489 let name = self.expect_ident_like()?;
5490 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
5494 self.advance();
5495 let mut names = Vec::new();
5496 loop {
5497 names.push(self.expect_ident_like()?);
5498 if matches!(self.peek(), Token::Comma) {
5499 self.advance();
5500 continue;
5501 }
5502 break;
5503 }
5504 if !matches!(self.peek(), Token::RParen) {
5505 return Err(self.err(format!(
5506 "expected ')' to close CTE column list, got {:?}",
5507 self.peek()
5508 )));
5509 }
5510 self.advance();
5511 names
5512 } else {
5513 Vec::new()
5514 };
5515 if !matches!(self.peek(), Token::As) {
5519 return Err(self.err(format!(
5520 "expected AS after CTE name {name:?}, got {:?}",
5521 self.peek()
5522 )));
5523 }
5524 self.advance();
5525 if !matches!(self.peek(), Token::LParen) {
5526 return Err(self.err(format!(
5527 "expected '(' after AS in WITH clause, got {:?}",
5528 self.peek()
5529 )));
5530 }
5531 self.advance();
5532 if !matches!(self.peek(), Token::Select) {
5533 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
5534 }
5535 let inner = self.parse_select_stmt()?;
5536 if !matches!(self.peek(), Token::RParen) {
5537 return Err(self.err(format!(
5538 "expected ')' after CTE body, got {:?}",
5539 self.peek()
5540 )));
5541 }
5542 self.advance();
5543 let Statement::Select(body) = inner else {
5544 unreachable!("parse_select_stmt returns Select")
5545 };
5546 ctes.push(crate::ast::Cte {
5547 name,
5548 body,
5549 recursive,
5550 column_overrides,
5551 });
5552 if matches!(self.peek(), Token::Comma) {
5553 self.advance();
5554 continue;
5555 }
5556 break;
5557 }
5558 if !matches!(self.peek(), Token::Select) {
5560 return Err(self.err(format!(
5561 "expected SELECT after WITH clause, got {:?}",
5562 self.peek()
5563 )));
5564 }
5565 let body_stmt = self.parse_select_stmt()?;
5566 let Statement::Select(mut body) = body_stmt else {
5567 unreachable!()
5568 };
5569 body.ctes = ctes;
5570 Ok(Statement::Select(body))
5571 }
5572
5573 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
5582 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
5586 None
5587 } else {
5588 Some(Box::new(self.parse_expr(0)?))
5589 };
5590 let mut branches: Vec<(Expr, Expr)> = Vec::new();
5591 loop {
5592 match self.peek() {
5593 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
5594 self.advance();
5595 let cond = self.parse_expr(0)?;
5596 match self.peek() {
5597 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
5598 self.advance();
5599 }
5600 other => {
5601 return Err(self.err(alloc::format!(
5602 "expected THEN after CASE WHEN <expr>, got {other:?}"
5603 )));
5604 }
5605 }
5606 let value = self.parse_expr(0)?;
5607 branches.push((cond, value));
5608 }
5609 _ => break,
5610 }
5611 }
5612 if branches.is_empty() {
5613 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
5614 }
5615 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
5616 {
5617 self.advance();
5618 Some(Box::new(self.parse_expr(0)?))
5619 } else {
5620 None
5621 };
5622 match self.peek() {
5623 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
5624 self.advance();
5625 }
5626 other => {
5627 return Err(self.err(alloc::format!(
5628 "expected END to close CASE expression, got {other:?}"
5629 )));
5630 }
5631 }
5632 Ok(Expr::Case {
5633 operand,
5634 branches,
5635 else_branch,
5636 })
5637 }
5638
5639 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
5640 if !matches!(self.peek(), Token::LParen) {
5641 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
5642 }
5643 self.advance();
5644 let inner = self.parse_select_stmt()?;
5645 if !matches!(self.peek(), Token::RParen) {
5646 return Err(self.err(format!(
5647 "expected ')' after EXISTS-subquery, got {:?}",
5648 self.peek()
5649 )));
5650 }
5651 self.advance();
5652 let Statement::Select(s) = inner else {
5653 unreachable!("parse_select_stmt returns Select")
5654 };
5655 Ok(Expr::Exists {
5656 subquery: Box::new(s),
5657 negated,
5658 })
5659 }
5660
5661 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
5662 self.advance(); if !matches!(self.peek(), Token::LParen) {
5664 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
5665 }
5666 self.advance();
5667 if matches!(self.peek(), Token::Select) {
5669 let inner = self.parse_select_stmt()?;
5670 if !matches!(self.peek(), Token::RParen) {
5671 return Err(self.err(format!(
5672 "expected ')' after IN-subquery, got {:?}",
5673 self.peek()
5674 )));
5675 }
5676 self.advance();
5677 let Statement::Select(s) = inner else {
5678 unreachable!("parse_select_stmt always returns Statement::Select")
5679 };
5680 return Ok(Expr::InSubquery {
5681 expr: Box::new(expr),
5682 subquery: Box::new(s),
5683 negated,
5684 });
5685 }
5686 let mut elements = Vec::new();
5687 if !matches!(self.peek(), Token::RParen) {
5688 loop {
5689 elements.push(self.parse_expr(0)?);
5690 match self.peek() {
5691 Token::Comma => {
5692 self.advance();
5693 }
5694 Token::RParen => break,
5695 other => {
5696 return Err(
5697 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
5698 );
5699 }
5700 }
5701 }
5702 }
5703 self.advance(); let target = Box::new(expr);
5705 let combined = if elements.is_empty() {
5706 Expr::Literal(Literal::Bool(false))
5707 } else {
5708 let mut iter = elements.into_iter();
5709 let first = iter.next().unwrap();
5710 let mut acc = Expr::Binary {
5711 lhs: target.clone(),
5712 op: BinOp::Eq,
5713 rhs: Box::new(first),
5714 };
5715 for elt in iter {
5716 acc = Expr::Binary {
5717 lhs: Box::new(acc),
5718 op: BinOp::Or,
5719 rhs: Box::new(Expr::Binary {
5720 lhs: target.clone(),
5721 op: BinOp::Eq,
5722 rhs: Box::new(elt),
5723 }),
5724 };
5725 }
5726 acc
5727 };
5728 Ok(maybe_not(combined, negated))
5729 }
5730
5731 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
5739 if !matches!(self.peek(), Token::LParen) {
5740 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
5741 }
5742 self.advance();
5743 let field_name = self.expect_ident_like()?;
5744 let field = match field_name.to_ascii_lowercase().as_str() {
5745 "year" => ExtractField::Year,
5746 "month" => ExtractField::Month,
5747 "day" => ExtractField::Day,
5748 "hour" => ExtractField::Hour,
5749 "minute" => ExtractField::Minute,
5750 "second" => ExtractField::Second,
5751 "microsecond" | "microseconds" => ExtractField::Microsecond,
5752 other => {
5753 return Err(self.err(format!(
5754 "unknown EXTRACT field {other:?}; \
5755 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
5756 )));
5757 }
5758 };
5759 if !matches!(self.peek(), Token::From) {
5760 return Err(self.err(format!(
5761 "expected FROM after EXTRACT field, got {:?}",
5762 self.peek()
5763 )));
5764 }
5765 self.advance();
5766 let source = self.parse_expr(0)?;
5767 if !matches!(self.peek(), Token::RParen) {
5768 return Err(self.err(format!(
5769 "expected ')' to close EXTRACT, got {:?}",
5770 self.peek()
5771 )));
5772 }
5773 self.advance();
5774 Ok(Expr::Extract {
5775 field,
5776 source: Box::new(source),
5777 })
5778 }
5779
5780 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
5785 let tok = self.advance();
5786 let Token::String(text) = tok else {
5787 return Err(self.err(format!(
5788 "expected string literal after INTERVAL, got {tok:?}"
5789 )));
5790 };
5791 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
5792 message: format!(
5793 "cannot parse INTERVAL {text:?}; \
5794 expected `<n> <unit> [<n> <unit> ...]` with units \
5795 microsecond[s], millisecond[s], second[s], minute[s], \
5796 hour[s], day[s], week[s], month[s], year[s]"
5797 ),
5798 token_pos: self.pos.saturating_sub(1),
5799 })?;
5800 Ok(Expr::Literal(Literal::Interval {
5801 months,
5802 micros,
5803 text,
5804 }))
5805 }
5806
5807 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
5808 let mut elems = Vec::new();
5809 if matches!(self.peek(), Token::RBracket) {
5810 self.advance();
5811 return Ok(Expr::Literal(Literal::Vector(elems)));
5812 }
5813 loop {
5814 let e = self.parse_expr(0)?;
5815 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
5816 message: format!("vector element must be a numeric literal, got {e:?}"),
5817 token_pos: self.pos,
5818 })?;
5819 elems.push(x);
5820 match self.peek() {
5821 Token::Comma => {
5822 self.advance();
5823 }
5824 Token::RBracket => {
5825 self.advance();
5826 break;
5827 }
5828 other => {
5829 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
5830 }
5831 }
5832 }
5833 Ok(Expr::Literal(Literal::Vector(elems)))
5834 }
5835
5836 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
5845 let Token::Ident(s) = self.peek().clone() else {
5846 return NullTreatment::Respect;
5847 };
5848 let is_ignore = s.eq_ignore_ascii_case("ignore");
5849 let is_respect = s.eq_ignore_ascii_case("respect");
5850 if !is_ignore && !is_respect {
5851 return NullTreatment::Respect;
5852 }
5853 if self.pos + 1 < self.tokens.len()
5856 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
5857 && s2.eq_ignore_ascii_case("nulls")
5858 {
5859 self.advance();
5860 self.advance();
5861 return if is_ignore {
5862 NullTreatment::Ignore
5863 } else {
5864 NullTreatment::Respect
5865 };
5866 }
5867 NullTreatment::Respect
5868 }
5869
5870 #[allow(clippy::type_complexity)] fn parse_over_clause(
5873 &mut self,
5874 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
5875 if !matches!(self.peek(), Token::LParen) {
5876 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
5877 }
5878 self.advance();
5879 let mut partition_by = Vec::new();
5880 let mut order_by = Vec::new();
5881 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
5883 && s.eq_ignore_ascii_case("partition")
5884 {
5885 self.advance();
5886 if !matches!(self.peek(), Token::By) {
5887 return Err(self.err(format!(
5888 "expected BY after PARTITION, got {:?}",
5889 self.peek()
5890 )));
5891 }
5892 self.advance();
5893 loop {
5894 partition_by.push(self.parse_expr(0)?);
5895 if matches!(self.peek(), Token::Comma) {
5896 self.advance();
5897 continue;
5898 }
5899 break;
5900 }
5901 }
5902 if matches!(self.peek(), Token::Order) {
5904 self.advance();
5905 if !matches!(self.peek(), Token::By) {
5906 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
5907 }
5908 self.advance();
5909 loop {
5910 let e = self.parse_expr(0)?;
5911 let desc = if matches!(self.peek(), Token::Desc) {
5912 self.advance();
5913 true
5914 } else if matches!(self.peek(), Token::Asc) {
5915 self.advance();
5916 false
5917 } else {
5918 false
5919 };
5920 order_by.push((e, desc));
5921 if matches!(self.peek(), Token::Comma) {
5922 self.advance();
5923 continue;
5924 }
5925 break;
5926 }
5927 }
5928 let mut frame: Option<WindowFrame> = None;
5932 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
5933 let kind = if s.eq_ignore_ascii_case("rows") {
5934 Some(FrameKind::Rows)
5935 } else if s.eq_ignore_ascii_case("range") {
5936 Some(FrameKind::Range)
5937 } else {
5938 None
5939 };
5940 if let Some(kind) = kind {
5941 self.advance();
5942 frame = Some(self.parse_frame_tail(kind)?);
5943 }
5944 }
5945 if !matches!(self.peek(), Token::RParen) {
5946 return Err(self.err(format!(
5947 "expected ')' to close OVER clause, got {:?}",
5948 self.peek()
5949 )));
5950 }
5951 self.advance();
5952 Ok((partition_by, order_by, frame))
5953 }
5954
5955 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
5961 if matches!(self.peek(), Token::Between) {
5962 self.advance();
5963 let start = self.parse_frame_bound()?;
5964 if !matches!(self.peek(), Token::And) {
5965 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
5966 }
5967 self.advance();
5968 let end = self.parse_frame_bound()?;
5969 Ok(WindowFrame {
5970 kind,
5971 start,
5972 end: Some(end),
5973 })
5974 } else {
5975 let start = self.parse_frame_bound()?;
5976 Ok(WindowFrame {
5977 kind,
5978 start,
5979 end: None,
5980 })
5981 }
5982 }
5983
5984 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
5987 if let Token::Integer(n) = *self.peek() {
5989 self.advance();
5990 let n: u64 = u64::try_from(n).map_err(|_| {
5991 self.err(format!(
5992 "invalid frame offset {n} — expected non-negative integer"
5993 ))
5994 })?;
5995 let dir = self.expect_ident_like()?;
5996 return if dir.eq_ignore_ascii_case("preceding") {
5997 Ok(FrameBound::OffsetPreceding(n))
5998 } else if dir.eq_ignore_ascii_case("following") {
5999 Ok(FrameBound::OffsetFollowing(n))
6000 } else {
6001 Err(self.err(format!(
6002 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
6003 )))
6004 };
6005 }
6006 let first = self.expect_ident_like()?;
6007 if first.eq_ignore_ascii_case("unbounded") {
6008 let dir = self.expect_ident_like()?;
6009 return if dir.eq_ignore_ascii_case("preceding") {
6010 Ok(FrameBound::UnboundedPreceding)
6011 } else if dir.eq_ignore_ascii_case("following") {
6012 Ok(FrameBound::UnboundedFollowing)
6013 } else {
6014 Err(self.err(format!(
6015 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
6016 )))
6017 };
6018 }
6019 if first.eq_ignore_ascii_case("current") {
6020 let row = self.expect_ident_like()?;
6021 if !row.eq_ignore_ascii_case("row") {
6022 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
6023 }
6024 return Ok(FrameBound::CurrentRow);
6025 }
6026 Err(self.err(format!(
6027 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
6028 )))
6029 }
6030
6031 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
6032 if matches!(self.peek(), Token::Dot) {
6033 self.advance();
6034 let name = self.expect_ident_like()?;
6035 if matches!(self.peek(), Token::LParen) {
6041 return self.finish_ident_atom(name);
6042 }
6043 return Ok(Expr::Column(ColumnName {
6044 qualifier: Some(first),
6045 name,
6046 }));
6047 }
6048 if matches!(self.peek(), Token::LParen) {
6049 self.advance();
6050 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
6054 self.advance();
6055 if !matches!(self.peek(), Token::RParen) {
6056 return Err(self.err(format!(
6057 "expected ')' after COUNT(*), got {:?}",
6058 self.peek()
6059 )));
6060 }
6061 self.advance();
6062 let null_treatment = self.parse_null_treatment_modifier();
6064 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
6065 && s.eq_ignore_ascii_case("over")
6066 {
6067 self.advance();
6068 let (partition_by, order_by, frame) = self.parse_over_clause()?;
6069 return Ok(Expr::WindowFunction {
6070 name: "count_star".into(),
6071 args: Vec::new(),
6072 partition_by,
6073 order_by,
6074 frame,
6075 null_treatment,
6076 });
6077 }
6078 return Ok(Expr::FunctionCall {
6079 name: "count_star".into(),
6080 args: Vec::new(),
6081 });
6082 }
6083 let mut args = Vec::new();
6085 if !matches!(self.peek(), Token::RParen) {
6086 loop {
6087 args.push(self.parse_expr(0)?);
6088 match self.peek() {
6089 Token::Comma => {
6090 self.advance();
6091 }
6092 Token::RParen => break,
6093 other => {
6094 return Err(self.err(format!(
6095 "expected ',' or ')' in function args, got {other:?}"
6096 )));
6097 }
6098 }
6099 }
6100 }
6101 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
6109 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
6110 && s.eq_ignore_ascii_case("over")
6111 {
6112 self.advance();
6113 let (partition_by, order_by, frame) = self.parse_over_clause()?;
6114 return Ok(Expr::WindowFunction {
6115 name: first,
6116 args,
6117 partition_by,
6118 order_by,
6119 frame,
6120 null_treatment,
6121 });
6122 }
6123 return Ok(Expr::FunctionCall { name: first, args });
6124 }
6125 let lc = first.to_ascii_lowercase();
6131 if matches!(
6132 lc.as_str(),
6133 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
6134 ) {
6135 return Ok(Expr::FunctionCall {
6136 name: lc,
6137 args: Vec::new(),
6138 });
6139 }
6140 Ok(Expr::Column(ColumnName {
6141 qualifier: None,
6142 name: first,
6143 }))
6144 }
6145}
6146
6147fn extract_first_column(expr: &Expr) -> Option<String> {
6155 match expr {
6156 Expr::Column(cn) => Some(cn.name.clone()),
6157 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
6158 Expr::Binary { lhs, rhs, .. } => {
6159 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
6160 }
6161 Expr::Unary { expr: e, .. } => extract_first_column(e),
6162 _ => None,
6163 }
6164}
6165
6166fn maybe_not(expr: Expr, negated: bool) -> Expr {
6167 if negated {
6168 Expr::Unary {
6169 op: UnOp::Not,
6170 expr: Box::new(expr),
6171 }
6172 } else {
6173 expr
6174 }
6175}
6176
6177fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
6178 let pair = match tok {
6179 Token::Or => (BinOp::Or, 1),
6180 Token::And => (BinOp::And, 2),
6181 Token::Eq => (BinOp::Eq, 4),
6182 Token::NotEq => (BinOp::NotEq, 4),
6183 Token::Lt => (BinOp::Lt, 4),
6184 Token::LtEq => (BinOp::LtEq, 4),
6185 Token::Gt => (BinOp::Gt, 4),
6186 Token::GtEq => (BinOp::GtEq, 4),
6187 Token::L2Distance => (BinOp::L2Distance, 5),
6190 Token::InnerProduct => (BinOp::InnerProduct, 5),
6191 Token::CosineDistance => (BinOp::CosineDistance, 5),
6192 Token::Plus => (BinOp::Add, 6),
6193 Token::Minus => (BinOp::Sub, 6),
6194 Token::Concat => (BinOp::Concat, 6),
6197 Token::Star => (BinOp::Mul, 7),
6198 Token::Slash => (BinOp::Div, 7),
6199 Token::JsonGet => (BinOp::JsonGet, 7),
6203 Token::JsonGetText => (BinOp::JsonGetText, 7),
6204 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
6205 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
6206 Token::JsonContains => (BinOp::JsonContains, 7),
6207 Token::TsMatch => (BinOp::TsMatch, 4),
6211 _ => return None,
6212 };
6213 Some(pair)
6214}
6215
6216#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
6217fn extract_numeric_literal(e: &Expr) -> Option<f32> {
6222 match e {
6223 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
6224 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
6225 Expr::Unary {
6226 op: UnOp::Neg,
6227 expr,
6228 } => extract_numeric_literal(expr).map(|x| -x),
6229 _ => None,
6230 }
6231}
6232
6233pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
6241 let parts: Vec<&str> = s.split_whitespace().collect();
6242 if parts.is_empty() || !parts.len().is_multiple_of(2) {
6243 return None;
6244 }
6245 let mut months: i32 = 0;
6246 let mut micros: i64 = 0;
6247 let mut i = 0;
6248 while i < parts.len() {
6249 let n: i64 = parts[i].parse().ok()?;
6250 let unit = parts[i + 1].to_ascii_lowercase();
6251 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
6252 match unit_stripped {
6253 "microsecond" => micros = micros.checked_add(n)?,
6254 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
6255 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
6256 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
6257 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
6258 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
6259 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
6260 "month" => {
6261 let n32 = i32::try_from(n).ok()?;
6262 months = months.checked_add(n32)?;
6263 }
6264 "year" => {
6265 let n32 = i32::try_from(n).ok()?;
6266 months = months.checked_add(n32.checked_mul(12)?)?;
6267 }
6268 _ => return None,
6269 }
6270 i += 2;
6271 }
6272 Some((months, micros))
6273}
6274
6275fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
6286 Some(match ident.to_ascii_lowercase().as_str() {
6287 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
6288 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
6289 "bigint" => ColumnTypeName::BigInt,
6290 "float" | "double" | "real" => ColumnTypeName::Float,
6291 "text" => ColumnTypeName::Text,
6292 "bool" | "boolean" => ColumnTypeName::Bool,
6293 "date" => ColumnTypeName::Date,
6294 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
6295 "timestamptz" => ColumnTypeName::Timestamptz,
6296 "json" => ColumnTypeName::Json,
6297 "jsonb" => ColumnTypeName::Jsonb,
6298 "bytea" | "bytes" => ColumnTypeName::Bytes,
6299 "tsvector" => ColumnTypeName::TsVector,
6300 "tsquery" => ColumnTypeName::TsQuery,
6301 _ => return None,
6302 })
6303}
6304
6305pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
6332 parse_plpgsql_body(body)
6333}
6334
6335fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
6336 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
6340 message: alloc::format!("plpgsql body lex error: {e}"),
6341 token_pos: 0,
6342 })?;
6343 let mut parser = Parser::new(tokens);
6344 parser.parse_plpgsql_block()
6345}
6346
6347#[cfg(test)]
6348mod tests {
6349 use super::*;
6350 use alloc::string::ToString;
6351
6352 fn parse(s: &str) -> Statement {
6353 parse_statement(s).expect("parse ok")
6354 }
6355
6356 fn lit_int(n: i64) -> Expr {
6357 Expr::Literal(Literal::Integer(n))
6358 }
6359
6360 fn col(name: &str) -> Expr {
6361 Expr::Column(ColumnName {
6362 qualifier: None,
6363 name: name.into(),
6364 })
6365 }
6366
6367 #[test]
6368 fn select_single_integer() {
6369 let s = parse("SELECT 1");
6370 let Statement::Select(s) = s else {
6371 panic!("expected SELECT")
6372 };
6373 assert_eq!(s.items.len(), 1);
6374 assert!(s.from.is_none());
6375 assert!(s.where_.is_none());
6376 }
6377
6378 #[test]
6379 fn select_multiple_literal_kinds() {
6380 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
6381 let Statement::Select(s) = s else {
6382 panic!("expected SELECT")
6383 };
6384 assert_eq!(s.items.len(), 5);
6385 }
6386
6387 #[test]
6388 fn select_wildcard_from_table() {
6389 let s = parse("SELECT * FROM users");
6390 let Statement::Select(s) = s else {
6391 panic!("expected SELECT")
6392 };
6393 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
6394 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
6395 }
6396
6397 #[test]
6398 fn select_with_table_alias() {
6399 let s = parse("SELECT * FROM users AS u");
6400 let Statement::Select(s) = s else {
6401 panic!("expected SELECT")
6402 };
6403 let t = &s.from.as_ref().unwrap().primary;
6404 assert_eq!(t.name, "users");
6405 assert_eq!(t.alias.as_deref(), Some("u"));
6406 }
6407
6408 #[test]
6409 fn select_with_where_eq() {
6410 let s = parse("SELECT a FROM t WHERE a = 1");
6411 let Statement::Select(s) = s else {
6412 panic!("expected SELECT")
6413 };
6414 let w = s.where_.unwrap();
6415 assert_eq!(
6416 w,
6417 Expr::Binary {
6418 lhs: Box::new(col("a")),
6419 op: BinOp::Eq,
6420 rhs: Box::new(lit_int(1)),
6421 }
6422 );
6423 }
6424
6425 #[test]
6426 fn arithmetic_precedence() {
6427 let s = parse("SELECT 1 + 2 * 3");
6428 let Statement::Select(s) = s else {
6429 panic!("expected SELECT")
6430 };
6431 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6432 panic!("wildcard?")
6433 };
6434 assert_eq!(
6435 expr,
6436 &Expr::Binary {
6437 lhs: Box::new(lit_int(1)),
6438 op: BinOp::Add,
6439 rhs: Box::new(Expr::Binary {
6440 lhs: Box::new(lit_int(2)),
6441 op: BinOp::Mul,
6442 rhs: Box::new(lit_int(3)),
6443 }),
6444 }
6445 );
6446 }
6447
6448 #[test]
6449 fn parentheses_override_precedence() {
6450 let s = parse("SELECT (1 + 2) * 3");
6451 let Statement::Select(s) = s else {
6452 panic!("expected SELECT")
6453 };
6454 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6455 panic!()
6456 };
6457 assert_eq!(
6458 expr,
6459 &Expr::Binary {
6460 lhs: Box::new(Expr::Binary {
6461 lhs: Box::new(lit_int(1)),
6462 op: BinOp::Add,
6463 rhs: Box::new(lit_int(2)),
6464 }),
6465 op: BinOp::Mul,
6466 rhs: Box::new(lit_int(3)),
6467 }
6468 );
6469 }
6470
6471 #[test]
6472 fn not_binds_below_comparison() {
6473 let s = parse("SELECT NOT a = 1 FROM t");
6475 let Statement::Select(s) = s else {
6476 panic!("expected SELECT")
6477 };
6478 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6479 panic!()
6480 };
6481 assert_eq!(
6482 expr,
6483 &Expr::Unary {
6484 op: UnOp::Not,
6485 expr: Box::new(Expr::Binary {
6486 lhs: Box::new(col("a")),
6487 op: BinOp::Eq,
6488 rhs: Box::new(lit_int(1)),
6489 }),
6490 }
6491 );
6492 }
6493
6494 #[test]
6495 fn unary_minus_binds_above_multiplication() {
6496 let s = parse("SELECT -a * 2 FROM t");
6498 let Statement::Select(s) = s else {
6499 panic!("expected SELECT")
6500 };
6501 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6502 panic!()
6503 };
6504 assert_eq!(
6505 expr,
6506 &Expr::Binary {
6507 lhs: Box::new(Expr::Unary {
6508 op: UnOp::Neg,
6509 expr: Box::new(col("a")),
6510 }),
6511 op: BinOp::Mul,
6512 rhs: Box::new(lit_int(2)),
6513 }
6514 );
6515 }
6516
6517 #[test]
6518 fn qualified_column() {
6519 let s = parse("SELECT t.col FROM t");
6520 let Statement::Select(s) = s else {
6521 panic!("expected SELECT")
6522 };
6523 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6524 panic!()
6525 };
6526 assert_eq!(
6527 expr,
6528 &Expr::Column(ColumnName {
6529 qualifier: Some("t".into()),
6530 name: "col".into()
6531 })
6532 );
6533 }
6534
6535 #[test]
6536 fn select_item_alias_with_as() {
6537 let s = parse("SELECT a AS y FROM t");
6538 let Statement::Select(s) = s else {
6539 panic!("expected SELECT")
6540 };
6541 let SelectItem::Expr { alias, .. } = &s.items[0] else {
6542 panic!()
6543 };
6544 assert_eq!(alias.as_deref(), Some("y"));
6545 }
6546
6547 #[test]
6548 fn trailing_semicolon_accepted() {
6549 let s = parse("SELECT 1;");
6550 let Statement::Select(s) = s else {
6551 panic!("expected SELECT")
6552 };
6553 assert_eq!(s.items.len(), 1);
6554 }
6555
6556 #[test]
6557 fn boolean_chain_with_and_or_not() {
6558 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
6560 let Statement::Select(s) = s else {
6561 panic!("expected SELECT")
6562 };
6563 let SelectItem::Expr { expr, .. } = &s.items[0] else {
6564 panic!()
6565 };
6566 let expected = Expr::Binary {
6567 lhs: Box::new(Expr::Unary {
6568 op: UnOp::Not,
6569 expr: Box::new(col("a")),
6570 }),
6571 op: BinOp::Or,
6572 rhs: Box::new(Expr::Binary {
6573 lhs: Box::new(col("b")),
6574 op: BinOp::And,
6575 rhs: Box::new(Expr::Unary {
6576 op: UnOp::Not,
6577 expr: Box::new(col("c")),
6578 }),
6579 }),
6580 };
6581 assert_eq!(expr, &expected);
6582 }
6583
6584 #[test]
6585 fn empty_input_errors() {
6586 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
6593 assert!(matches!(
6594 parse_statement(" \n\t ").unwrap(),
6595 Statement::Empty
6596 ));
6597 assert!(parse_statement("SELECT FROM WHERE").is_err());
6599 }
6600
6601 #[test]
6602 fn unmatched_paren_errors() {
6603 assert!(parse_statement("SELECT (1 + 2").is_err());
6604 }
6605
6606 #[test]
6607 fn display_round_trip_simple_select() {
6608 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
6609 let text = original.to_string();
6610 let again = parse_statement(&text).expect("re-parse");
6611 assert_eq!(original, again);
6612 }
6613
6614 #[test]
6617 fn create_table_single_column() {
6618 let s = parse("CREATE TABLE foo (a INT)");
6619 let Statement::CreateTable(c) = s else {
6620 panic!("expected CreateTable")
6621 };
6622 assert_eq!(c.name, "foo");
6623 assert_eq!(c.columns.len(), 1);
6624 assert_eq!(c.columns[0].name, "a");
6625 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6626 assert!(c.columns[0].nullable);
6627 }
6628
6629 #[test]
6630 fn create_table_multi_column_with_not_null_mix() {
6631 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
6632 let Statement::CreateTable(c) = s else {
6633 panic!()
6634 };
6635 assert_eq!(c.columns.len(), 4);
6636 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
6637 assert!(!c.columns[0].nullable);
6638 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
6639 assert!(c.columns[1].nullable);
6640 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
6641 assert!(!c.columns[2].nullable);
6642 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
6643 }
6644
6645 #[test]
6646 fn create_table_bigint_supported() {
6647 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
6648 let Statement::CreateTable(c) = s else {
6649 panic!()
6650 };
6651 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
6652 }
6653
6654 #[test]
6655 fn create_table_vector_default_is_f32() {
6656 let s = parse("CREATE TABLE t (v VECTOR(128))");
6657 let Statement::CreateTable(c) = s else {
6658 panic!()
6659 };
6660 assert_eq!(
6661 c.columns[0].ty,
6662 ColumnTypeName::Vector {
6663 dim: 128,
6664 encoding: VecEncoding::F32,
6665 },
6666 );
6667 }
6668
6669 #[test]
6670 fn create_table_vector_using_sq8() {
6671 for sql in [
6674 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
6675 "CREATE TABLE t (v VECTOR(128) using sq8)",
6676 ] {
6677 let s = parse(sql);
6678 let Statement::CreateTable(c) = s else {
6679 panic!()
6680 };
6681 assert_eq!(
6682 c.columns[0].ty,
6683 ColumnTypeName::Vector {
6684 dim: 128,
6685 encoding: VecEncoding::Sq8,
6686 },
6687 "{sql}",
6688 );
6689 }
6690 }
6691
6692 #[test]
6693 fn create_table_vector_using_unknown_errors() {
6694 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
6703 assert!(
6704 err.message.contains("USING")
6705 || err.message.contains("using")
6706 || err.message.contains("')'")
6707 || err.message.contains("','"),
6708 "expected USING/column-list rejection, got: {}",
6709 err.message
6710 );
6711 }
6712
6713 #[test]
6714 fn vector_using_sq8_display_roundtrips() {
6715 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
6718 let Statement::CreateTable(c) = s else {
6719 panic!()
6720 };
6721 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
6722 }
6723
6724 #[test]
6725 fn parser_recognises_placeholders() {
6726 use crate::ast::{Expr, SelectItem, Statement};
6727 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
6729 let Statement::Select(sel) = s else { panic!() };
6730 assert!(matches!(
6731 sel.items[0],
6732 SelectItem::Expr {
6733 expr: Expr::Placeholder(1),
6734 alias: None
6735 }
6736 ));
6737 let SelectItem::Expr {
6739 expr: Expr::Binary { lhs, rhs, .. },
6740 ..
6741 } = &sel.items[1]
6742 else {
6743 panic!()
6744 };
6745 assert!(matches!(**lhs, Expr::Placeholder(2)));
6746 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
6747 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
6749 panic!()
6750 };
6751 assert!(matches!(**rhs, Expr::Placeholder(3)));
6752 }
6753
6754 #[test]
6755 fn parser_rejects_dollar_zero() {
6756 assert!(parse_statement("SELECT $0").is_err());
6758 }
6759
6760 #[test]
6761 fn placeholder_display_roundtrips() {
6762 let s = parse("SELECT $42 FROM t");
6765 let printed = s.to_string();
6766 assert!(printed.contains("$42"));
6767 let again = parse(&printed);
6768 assert_eq!(s, again);
6769 }
6770
6771 #[test]
6772 fn alter_index_rebuild_bare() {
6773 use crate::ast::{AlterIndexTarget, Statement};
6774 let s = parse("ALTER INDEX my_idx REBUILD");
6775 let Statement::AlterIndex(a) = s else {
6776 panic!("expected AlterIndex, got {s:?}")
6777 };
6778 assert_eq!(a.name, "my_idx");
6779 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
6780 }
6781
6782 #[test]
6783 fn alter_index_rebuild_with_encoding() {
6784 use crate::ast::{AlterIndexTarget, Statement};
6785 for (sql, want) in [
6786 (
6787 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
6788 VecEncoding::F32,
6789 ),
6790 (
6791 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
6792 VecEncoding::Sq8,
6793 ),
6794 (
6795 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6796 VecEncoding::F16,
6797 ),
6798 ] {
6799 let s = parse(sql);
6800 let Statement::AlterIndex(a) = s else {
6801 panic!("{sql}: expected AlterIndex")
6802 };
6803 assert_eq!(a.name, "my_idx");
6804 assert_eq!(
6805 a.target,
6806 AlterIndexTarget::Rebuild {
6807 encoding: Some(want)
6808 },
6809 "{sql}"
6810 );
6811 }
6812 }
6813
6814 #[test]
6815 fn alter_index_rebuild_unknown_encoding_errors() {
6816 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
6817 assert!(
6818 err.message.contains("unknown vector encoding"),
6819 "got: {}",
6820 err.message
6821 );
6822 }
6823
6824 #[test]
6825 fn alter_index_rebuild_display_roundtrips() {
6826 for (input, want) in [
6827 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
6828 (
6829 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6830 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
6831 ),
6832 (
6833 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6834 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
6835 ),
6836 ] {
6837 let s = parse(input);
6838 assert_eq!(s.to_string(), want);
6839 }
6840 }
6841
6842 #[test]
6843 fn create_table_unknown_type_errors() {
6844 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
6847 assert!(err.message.contains("unsupported column type"));
6848 }
6849
6850 #[test]
6851 fn create_table_missing_table_keyword_errors() {
6852 assert!(parse_statement("CREATE x (a INT)").is_err());
6853 }
6854
6855 #[test]
6856 fn insert_single_value() {
6857 let s = parse("INSERT INTO foo VALUES (42)");
6858 let Statement::Insert(i) = s else {
6859 panic!("expected Insert")
6860 };
6861 assert_eq!(i.table, "foo");
6862 assert_eq!(i.rows.len(), 1);
6863 assert_eq!(i.rows[0].len(), 1);
6864 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
6865 }
6866
6867 #[test]
6868 fn insert_multi_value_with_mixed_literals() {
6869 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
6870 let Statement::Insert(i) = s else { panic!() };
6871 assert_eq!(i.rows.len(), 1);
6872 assert_eq!(i.rows[0].len(), 5);
6873 }
6874
6875 #[test]
6876 fn insert_missing_into_errors() {
6877 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
6878 }
6879
6880 #[test]
6881 fn create_table_round_trip() {
6882 let original =
6883 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
6884 let text = original.to_string();
6885 let again = parse_statement(&text).expect("re-parse");
6886 assert_eq!(original, again);
6887 }
6888
6889 #[test]
6890 fn insert_round_trip_with_negation_and_string() {
6891 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
6892 let text = original.to_string();
6893 let again = parse_statement(&text).expect("re-parse");
6894 assert_eq!(original, again);
6895 }
6896
6897 #[test]
6898 fn unknown_keyword_at_statement_start_errors() {
6899 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
6902 assert!(err.message.contains("expected SELECT"));
6903 }
6904
6905 #[test]
6908 fn create_index_basic() {
6909 let s = parse("CREATE INDEX idx_id ON users (id)");
6910 let Statement::CreateIndex(c) = s else {
6911 panic!("expected CreateIndex")
6912 };
6913 assert_eq!(c.name, "idx_id");
6914 assert_eq!(c.table, "users");
6915 assert_eq!(c.column, "id");
6916 }
6917
6918 #[test]
6919 fn create_index_missing_on_errors() {
6920 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
6921 }
6922
6923 #[test]
6924 fn create_index_missing_paren_errors() {
6925 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
6926 }
6927
6928 #[test]
6929 fn create_index_round_trip() {
6930 let original = parse("CREATE INDEX by_name ON users (name)");
6931 let again = parse_statement(&original.to_string()).unwrap();
6932 assert_eq!(original, again);
6933 }
6934
6935 #[test]
6938 fn create_unique_index_basic() {
6939 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
6940 let Statement::CreateIndex(c) = s else {
6941 panic!("expected CreateIndex");
6942 };
6943 assert!(c.is_unique);
6944 assert_eq!(c.column, "a");
6945 assert!(c.partial_predicate.is_none());
6946 }
6947
6948 #[test]
6949 fn create_unique_index_partial() {
6950 let s = parse(
6952 "CREATE UNIQUE INDEX idx_email_templates_user_default \
6953 ON email_templates (user_address) WHERE is_default = true",
6954 );
6955 let Statement::CreateIndex(c) = s else {
6956 panic!("expected CreateIndex");
6957 };
6958 assert!(c.is_unique);
6959 assert_eq!(c.table, "email_templates");
6960 assert_eq!(c.column, "user_address");
6961 assert!(c.partial_predicate.is_some());
6962 }
6963
6964 #[test]
6965 fn create_unique_index_composite_with_predicate() {
6966 let s = parse(
6968 "CREATE UNIQUE INDEX uq_calendar_events_instance \
6969 ON calendar_events (calendar_id, uid, recurrence_id) \
6970 WHERE recurrence_id IS NOT NULL",
6971 );
6972 let Statement::CreateIndex(c) = s else {
6973 panic!("expected CreateIndex");
6974 };
6975 assert!(c.is_unique);
6976 assert_eq!(c.column, "calendar_id");
6977 assert_eq!(
6978 c.extra_columns,
6979 vec!["uid".to_string(), "recurrence_id".to_string()]
6980 );
6981 assert!(c.partial_predicate.is_some());
6982 }
6983
6984 #[test]
6985 fn create_unique_index_using_btree_ok() {
6986 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
6987 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
6988 }
6989
6990 #[test]
6991 fn create_unique_index_using_hnsw_rejected() {
6992 let err =
6993 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
6994 assert!(err.message.contains("UNIQUE"), "{}", err.message);
6995 }
6996
6997 #[test]
6998 fn create_unique_index_round_trip() {
6999 let original = parse(
7000 "CREATE UNIQUE INDEX uq_calendar_events_master \
7001 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
7002 );
7003 let again = parse_statement(&original.to_string()).unwrap();
7004 assert_eq!(original, again);
7005 }
7006
7007 #[test]
7008 fn create_unique_without_index_errors() {
7009 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
7010 assert!(err.message.contains("INDEX"), "{}", err.message);
7011 }
7012
7013 #[test]
7016 fn create_table_bytea_column() {
7017 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
7018 let Statement::CreateTable(c) = s else {
7019 panic!("expected CreateTable");
7020 };
7021 assert_eq!(c.columns.len(), 2);
7022 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
7023 assert!(!c.columns[1].nullable);
7024 }
7025
7026 #[test]
7027 fn create_table_bytes_alias_column() {
7028 let s = parse("CREATE TABLE t (blob BYTES)");
7029 let Statement::CreateTable(c) = s else {
7030 panic!("expected CreateTable");
7031 };
7032 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
7033 }
7034
7035 #[test]
7036 fn bytea_round_trip_display() {
7037 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
7038 let again = parse_statement(&original.to_string()).unwrap();
7039 assert_eq!(original, again);
7040 }
7041
7042 #[test]
7045 fn begin_commit_rollback_parse_as_unit_variants() {
7046 assert_eq!(parse("BEGIN"), Statement::Begin);
7047 assert_eq!(parse("COMMIT"), Statement::Commit);
7048 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
7049 assert_eq!(parse("BEGIN;"), Statement::Begin);
7051 }
7052
7053 #[test]
7056 fn inner_product_binop_parses() {
7057 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
7058 let Statement::Select(s) = s else { panic!() };
7059 let SelectItem::Expr { expr, .. } = &s.items[0] else {
7060 panic!()
7061 };
7062 assert!(matches!(
7063 expr,
7064 Expr::Binary {
7065 op: BinOp::InnerProduct,
7066 ..
7067 }
7068 ));
7069 }
7070
7071 #[test]
7072 fn cosine_distance_binop_parses() {
7073 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
7074 let Statement::Select(s) = s else { panic!() };
7075 let SelectItem::Expr { expr, .. } = &s.items[0] else {
7076 panic!()
7077 };
7078 assert!(matches!(
7079 expr,
7080 Expr::Binary {
7081 op: BinOp::CosineDistance,
7082 ..
7083 }
7084 ));
7085 }
7086
7087 #[test]
7088 fn vector_cast_postfix_wraps_string_literal() {
7089 let s = parse("SELECT '[1,2,3]'::vector FROM t");
7090 let Statement::Select(s) = s else { panic!() };
7091 let SelectItem::Expr { expr, .. } = &s.items[0] else {
7092 panic!()
7093 };
7094 assert!(matches!(
7095 expr,
7096 Expr::Cast {
7097 target: CastTarget::Vector,
7098 ..
7099 }
7100 ));
7101 }
7102
7103 #[test]
7104 fn unsupported_cast_target_errors() {
7105 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
7107 assert!(err.message.contains("unsupported cast target"));
7108 }
7109
7110 #[test]
7111 fn tx_statements_round_trip() {
7112 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
7113 let original = parse(q);
7114 let again = parse_statement(&original.to_string()).unwrap();
7115 assert_eq!(original, again);
7116 }
7117 }
7118
7119 #[test]
7120 fn interval_text_parsing_units() {
7121 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
7123 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
7124 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
7125 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
7126 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
7128 assert_eq!(
7129 parse_interval_text("1 day 2 hours"),
7130 Some((0, 86_400_000_000 + 7_200_000_000))
7131 );
7132 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
7134 assert_eq!(parse_interval_text(""), None);
7136 assert_eq!(parse_interval_text("garbage"), None);
7137 assert_eq!(parse_interval_text("1 fortnight"), None);
7138 assert_eq!(parse_interval_text("1"), None);
7139 }
7140
7141 #[test]
7142 fn interval_literal_roundtrips_via_display() {
7143 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
7144 let s = parsed.to_string();
7145 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
7147 let again = parse_statement(&s).unwrap();
7149 assert_eq!(parsed, again);
7150 }
7151
7152 #[test]
7155 fn parser_recognises_create_publication_bare() {
7156 let s = parse("CREATE PUBLICATION pub_a");
7157 let Statement::CreatePublication(p) = s else {
7158 panic!("expected CreatePublication, got {s:?}")
7159 };
7160 assert_eq!(p.name, "pub_a");
7161 assert_eq!(p.scope, PublicationScope::AllTables);
7162 }
7163
7164 #[test]
7165 fn parser_recognises_create_publication_for_all_tables() {
7166 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
7167 let Statement::CreatePublication(p) = s else {
7168 panic!("expected CreatePublication, got {s:?}")
7169 };
7170 assert_eq!(p.name, "pub_a");
7171 assert_eq!(p.scope, PublicationScope::AllTables);
7172 }
7173
7174 #[test]
7175 fn parser_recognises_drop_publication() {
7176 let s = parse("DROP PUBLICATION pub_a");
7177 let Statement::DropPublication(name) = s else {
7178 panic!("expected DropPublication, got {s:?}")
7179 };
7180 assert_eq!(name, "pub_a");
7181 }
7182
7183 #[test]
7184 fn parser_recognises_for_table_list() {
7185 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
7186 let Statement::CreatePublication(p) = s else {
7187 panic!("expected CreatePublication, got {s:?}")
7188 };
7189 assert_eq!(p.name, "pub_a");
7190 let PublicationScope::ForTables(ts) = p.scope else {
7191 panic!("expected ForTables scope")
7192 };
7193 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
7194 }
7195
7196 #[test]
7197 fn parser_recognises_for_tables_plural() {
7198 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
7200 let Statement::CreatePublication(p) = s else {
7201 panic!("expected CreatePublication, got {s:?}")
7202 };
7203 let PublicationScope::ForTables(ts) = p.scope else {
7204 panic!("expected ForTables")
7205 };
7206 assert_eq!(ts, alloc::vec!["t1", "t2"]);
7207 }
7208
7209 #[test]
7210 fn parser_recognises_for_all_tables_except_list() {
7211 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
7212 let Statement::CreatePublication(p) = s else {
7213 panic!()
7214 };
7215 let PublicationScope::AllTablesExcept(ts) = p.scope else {
7216 panic!("expected AllTablesExcept")
7217 };
7218 assert_eq!(ts, alloc::vec!["t1", "t2"]);
7219 }
7220
7221 #[test]
7222 fn parser_rejects_for_table_with_empty_list() {
7223 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
7225 .expect_err("must error on empty list");
7226 assert!(!err.message.is_empty());
7229 }
7230
7231 #[test]
7232 fn parser_recognises_show_publications() {
7233 let s = parse("SHOW PUBLICATIONS");
7236 assert!(matches!(s, Statement::ShowPublications));
7237 }
7238
7239 #[test]
7242 fn parser_recognises_create_subscription_single_publication() {
7243 let s = parse(
7244 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
7245 );
7246 let Statement::CreateSubscription(c) = s else {
7247 panic!("expected CreateSubscription, got {s:?}")
7248 };
7249 assert_eq!(c.name, "sub_a");
7250 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
7251 assert_eq!(c.publications, alloc::vec!["pub_a"]);
7252 }
7253
7254 #[test]
7255 fn parser_recognises_create_subscription_multi_publication() {
7256 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
7257 let Statement::CreateSubscription(c) = s else {
7258 panic!()
7259 };
7260 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
7261 }
7262
7263 #[test]
7264 fn parser_rejects_create_subscription_missing_connection() {
7265 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
7266 .expect_err("must error on missing CONNECTION");
7267 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
7268 }
7269
7270 #[test]
7271 fn parser_rejects_create_subscription_missing_publication() {
7272 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
7273 .expect_err("must error on missing PUBLICATION");
7274 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
7275 }
7276
7277 #[test]
7278 fn parser_recognises_drop_subscription() {
7279 let s = parse("DROP SUBSCRIPTION sub_a");
7280 let Statement::DropSubscription(name) = s else {
7281 panic!("expected DropSubscription, got {s:?}")
7282 };
7283 assert_eq!(name, "sub_a");
7284 }
7285
7286 #[test]
7287 fn parser_recognises_show_subscriptions() {
7288 let s = parse("SHOW SUBSCRIPTIONS");
7289 assert!(matches!(s, Statement::ShowSubscriptions));
7290 }
7291
7292 #[test]
7293 fn parser_recognises_wait_for_wal_position_no_timeout() {
7294 let s = parse("WAIT FOR WAL POSITION 12345");
7295 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
7296 panic!("expected WaitForWalPosition, got {s:?}")
7297 };
7298 assert_eq!(pos, 12345);
7299 assert!(timeout_ms.is_none());
7300 }
7301
7302 #[test]
7303 fn parser_recognises_wait_for_wal_position_with_timeout() {
7304 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
7305 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
7306 panic!()
7307 };
7308 assert_eq!(pos, 67890);
7309 assert_eq!(timeout_ms, Some(5000));
7310 }
7311
7312 #[test]
7313 fn parser_rejects_wait_with_negative_position() {
7314 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
7320 assert!(!err.message.is_empty());
7321 }
7322
7323 #[test]
7324 fn parser_recognises_bare_analyze() {
7325 let s = parse("ANALYZE");
7326 assert!(matches!(s, Statement::Analyze(None)));
7327 }
7328
7329 #[test]
7330 fn parser_recognises_analyze_with_table() {
7331 let s = parse("ANALYZE users");
7332 let Statement::Analyze(Some(name)) = s else {
7333 panic!("expected Analyze, got {s:?}")
7334 };
7335 assert_eq!(name, "users");
7336 }
7337
7338 #[test]
7339 fn parser_recognises_analyze_with_quoted_table() {
7340 let s = parse("ANALYZE \"Mixed Case\"");
7341 let Statement::Analyze(Some(name)) = s else {
7342 panic!()
7343 };
7344 assert_eq!(name, "Mixed Case");
7345 }
7346
7347 #[test]
7348 fn parser_rejects_analyze_with_garbage_token() {
7349 let err = parse_statement("ANALYZE 42").expect_err("must error");
7350 assert!(!err.message.is_empty());
7351 }
7352
7353 #[test]
7354 fn analyze_display_roundtrips() {
7355 for sql in ["ANALYZE", "ANALYZE users"] {
7356 let s = parse(sql);
7357 let printed = s.to_string();
7358 let again = parse_statement(&printed)
7359 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7360 assert_eq!(s, again);
7361 }
7362 }
7363
7364 #[test]
7365 fn wait_for_display_roundtrips() {
7366 for sql in [
7367 "WAIT FOR WAL POSITION 12345",
7368 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
7369 ] {
7370 let s = parse(sql);
7371 let printed = s.to_string();
7372 let again = parse_statement(&printed)
7373 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7374 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7375 }
7376 }
7377
7378 #[test]
7379 fn subscription_ddl_display_roundtrips() {
7380 for sql in [
7381 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
7382 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
7383 "DROP SUBSCRIPTION sub_a",
7384 "SHOW SUBSCRIPTIONS",
7385 ] {
7386 let s = parse(sql);
7387 let printed = s.to_string();
7388 let again = parse_statement(&printed)
7389 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7390 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7391 }
7392 }
7393
7394 #[test]
7395 fn parser_drop_dispatches_user_vs_publication() {
7396 let s = parse("DROP USER 'alice'");
7399 let Statement::DropUser(name) = s else {
7400 panic!("expected DropUser, got {s:?}")
7401 };
7402 assert_eq!(name, "alice");
7403 let s = parse("DROP PUBLICATION p1");
7405 assert!(matches!(s, Statement::DropPublication(_)));
7406 }
7407
7408 #[test]
7409 fn publication_ddl_display_roundtrips() {
7410 for sql in [
7413 "CREATE PUBLICATION pub_a",
7414 "CREATE PUBLICATION pub_a FOR ALL TABLES",
7415 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
7416 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
7417 "DROP PUBLICATION pub_a",
7418 "SHOW PUBLICATIONS",
7419 ] {
7420 let s = parse(sql);
7421 let printed = s.to_string();
7422 let again = parse_statement(&printed)
7423 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7424 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7425 }
7426 }
7427
7428 #[test]
7431 fn create_function_returns_trigger_plpgsql_minimal() {
7432 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
7433 let s = parse(sql);
7434 let Statement::CreateFunction(f) = s else {
7435 panic!("expected CreateFunction");
7436 };
7437 assert_eq!(f.name, "noop");
7438 assert!(!f.or_replace);
7439 assert!(f.args.is_empty());
7440 assert!(matches!(f.returns, FunctionReturn::Trigger));
7441 assert_eq!(f.language, "plpgsql");
7442 let FunctionBody::PlPgSql(block) = f.body else {
7443 panic!("expected PlPgSql body");
7444 };
7445 assert_eq!(block.statements.len(), 1);
7446 assert!(matches!(
7447 block.statements[0],
7448 PlPgSqlStmt::Return(ReturnTarget::New)
7449 ));
7450 }
7451
7452 #[test]
7453 fn create_function_or_replace_with_assignment() {
7454 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
7457BEGIN
7458 NEW.search_vector := to_tsvector('english', NEW.subject);
7459 RETURN NEW;
7460END;
7461$$";
7462 let s = parse(sql);
7463 let Statement::CreateFunction(f) = s else {
7464 panic!("expected CreateFunction");
7465 };
7466 assert!(f.or_replace);
7467 let FunctionBody::PlPgSql(block) = &f.body else {
7468 panic!("expected PlPgSql body");
7469 };
7470 assert_eq!(block.statements.len(), 2);
7471 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
7473 panic!("expected Assign as first stmt");
7474 };
7475 match target {
7476 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
7477 other => panic!("expected NEW.col, got {other:?}"),
7478 }
7479 assert!(matches!(
7481 block.statements[1],
7482 PlPgSqlStmt::Return(ReturnTarget::New)
7483 ));
7484 }
7485
7486 #[test]
7487 fn create_trigger_after_insert_or_update() {
7488 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
7489 let s = parse(sql);
7490 let Statement::CreateTrigger(t) = s else {
7491 panic!("expected CreateTrigger");
7492 };
7493 assert_eq!(t.name, "tg");
7494 assert_eq!(t.table, "messages");
7495 assert_eq!(t.timing, TriggerTiming::After);
7496 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
7497 assert_eq!(t.for_each, TriggerForEach::Row);
7498 assert_eq!(t.function, "update_sv");
7499 }
7500
7501 #[test]
7502 fn create_trigger_before_delete_execute_procedure_alias() {
7503 let sql =
7505 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
7506 let s = parse(sql);
7507 let Statement::CreateTrigger(t) = s else {
7508 panic!("expected CreateTrigger");
7509 };
7510 assert_eq!(t.timing, TriggerTiming::Before);
7511 assert_eq!(t.events, vec![TriggerEvent::Delete]);
7512 }
7513
7514 #[test]
7515 fn drop_trigger_if_exists_round_trips() {
7516 let s = Statement::DropTrigger {
7521 name: "tg".into(),
7522 table: "messages".into(),
7523 if_exists: true,
7524 };
7525 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
7526 }
7527
7528 #[test]
7529 fn trigger_ddl_display_roundtrips_through_parser() {
7530 for sql in [
7534 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
7535 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
7536 ] {
7537 let s = parse(sql);
7538 let printed = s.to_string();
7539 let again = parse_statement(&printed)
7540 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
7541 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
7542 }
7543 }
7544}