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_vector_opclass_name(name: &str) -> bool {
37 let lc = name.to_ascii_lowercase();
38 matches!(
39 lc.as_str(),
40 "vector_cosine_ops"
41 | "vector_l2_ops"
42 | "vector_ip_ops"
43 | "halfvec_cosine_ops"
44 | "halfvec_l2_ops"
45 | "halfvec_ip_ops"
46 | "sq8_cosine_ops"
47 | "sq8_l2_ops"
48 | "sq8_ip_ops"
49 )
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct ParseError {
54 pub message: String,
55 pub token_pos: usize,
57}
58
59impl fmt::Display for ParseError {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(
62 f,
63 "parse error at token #{}: {}",
64 self.token_pos, self.message
65 )
66 }
67}
68
69impl From<LexError> for ParseError {
70 fn from(e: LexError) -> Self {
71 Self {
72 message: format!("lex: {e}"),
73 token_pos: 0,
74 }
75 }
76}
77
78pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
84 let tokens = lexer::tokenize(input)?;
85 let mut p = Parser::new(tokens);
86 let expr = p.parse_expr(0)?;
87 p.expect_eof()?;
88 Ok(expr)
89}
90
91pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
94 let tokens = lexer::tokenize(input)?;
95 let mut p = Parser::new(tokens);
96 let stmt = p.parse_one_statement()?;
97 if matches!(p.peek(), Token::Semicolon) {
98 p.advance();
99 }
100 p.expect_eof()?;
101 Ok(stmt)
102}
103
104struct Parser {
105 tokens: Vec<Token>,
106 pos: usize,
107}
108
109impl Parser {
110 fn new(tokens: Vec<Token>) -> Self {
111 Self { tokens, pos: 0 }
112 }
113
114 fn peek(&self) -> &Token {
115 &self.tokens[self.pos]
117 }
118
119 fn advance(&mut self) -> Token {
120 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
121 if self.pos + 1 < self.tokens.len() {
122 self.pos += 1;
123 }
124 t
125 }
126
127 fn err(&self, message: String) -> ParseError {
128 ParseError {
129 message,
130 token_pos: self.pos,
131 }
132 }
133
134 fn expect_eof(&self) -> Result<(), ParseError> {
135 if matches!(self.peek(), Token::Eof) {
136 Ok(())
137 } else {
138 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
139 }
140 }
141
142 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
143 match self.advance() {
144 Token::Ident(s) | Token::QuotedIdent(s) => Ok(s),
145 other => Err(ParseError {
146 message: format!("expected identifier, got {other:?}"),
147 token_pos: self.pos.saturating_sub(1),
148 }),
149 }
150 }
151
152 #[allow(clippy::too_many_lines)]
153 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
154 match self.peek() {
155 Token::Select => self.parse_select_stmt(),
156 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
162 self.advance();
163 match self.advance() {
166 Token::String(_) => {}
167 other => {
168 return Err(self.err(alloc::format!(
169 "expected dollar-quoted body after DO, got {other:?}"
170 )));
171 }
172 }
173 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
175 self.advance();
176 let _ = self.expect_ident_like()?;
177 }
178 Ok(Statement::DoBlock)
179 }
180 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
184 self.advance();
185 self.parse_with_cte_then_select()
186 }
187 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
190 self.advance();
191 let mut analyze = false;
192 let mut suggest = false;
193 if matches!(self.peek(), Token::LParen) {
195 self.advance();
196 let opt = match self.peek().clone() {
197 Token::Ident(s) | Token::QuotedIdent(s) => s,
198 other => {
199 return Err(self.err(format!(
200 "expected option keyword inside EXPLAIN (…), got {other:?}"
201 )));
202 }
203 };
204 if !opt.eq_ignore_ascii_case("suggest") {
205 return Err(self.err(format!(
206 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
207 )));
208 }
209 self.advance();
210 if !matches!(self.peek(), Token::RParen) {
211 return Err(self.err(format!(
212 "expected ')' after EXPLAIN option, got {:?}",
213 self.peek()
214 )));
215 }
216 self.advance();
217 suggest = true;
218 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
219 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
220 {
221 self.advance();
222 analyze = true;
223 }
224 let inner = self.parse_select_stmt()?;
225 let Statement::Select(s) = inner else {
226 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
227 };
228 Ok(Statement::Explain(crate::ast::ExplainStatement {
229 analyze,
230 inner: Box::new(s),
231 suggest,
232 }))
233 }
234 Token::Create => self.parse_create_stmt(),
235 Token::Insert => self.parse_insert_stmt(),
236 Token::Begin => {
237 self.advance();
238 Ok(Statement::Begin)
239 }
240 Token::Commit => {
241 self.advance();
242 Ok(Statement::Commit)
243 }
244 Token::Rollback => {
245 self.advance();
246 if matches!(self.peek(), Token::To) {
250 self.advance();
251 if matches!(self.peek(), Token::Savepoint) {
252 self.advance();
253 }
254 let name = self.expect_ident_like()?;
255 Ok(Statement::RollbackToSavepoint(name))
256 } else {
257 Ok(Statement::Rollback)
258 }
259 }
260 Token::Savepoint => {
261 self.advance();
262 let name = self.expect_ident_like()?;
263 Ok(Statement::Savepoint(name))
264 }
265 Token::Release => {
266 self.advance();
267 if matches!(self.peek(), Token::Savepoint) {
270 self.advance();
271 }
272 let name = self.expect_ident_like()?;
273 Ok(Statement::ReleaseSavepoint(name))
274 }
275 Token::Show => {
276 self.advance();
277 let target = match self.advance() {
283 Token::Tables => "tables".to_string(),
284 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
285 other => {
286 return Err(self.err(format!(
287 "expected SHOW target, got {other:?}"
288 )));
289 }
290 };
291 match target.as_str() {
292 "tables" => Ok(Statement::ShowTables),
293 "users" => Ok(Statement::ShowUsers),
294 "publications" => Ok(Statement::ShowPublications),
299 "subscriptions" => Ok(Statement::ShowSubscriptions),
301 "columns" => {
302 if !matches!(self.peek(), Token::From) {
303 return Err(self.err(format!(
304 "expected FROM after SHOW COLUMNS, got {:?}",
305 self.peek()
306 )));
307 }
308 self.advance();
309 let table = self.expect_ident_like()?;
310 Ok(Statement::ShowColumns(table))
311 }
312 other => Err(self.err(format!(
313 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
314 ))),
315 }
316 }
317 Token::Drop => {
323 self.advance();
324 match self.peek() {
325 Token::Publication => {
326 self.advance();
327 let name = self.expect_ident_or_string()?;
328 Ok(Statement::DropPublication(name))
329 }
330 Token::Subscription => {
331 self.advance();
332 let name = self.expect_ident_or_string()?;
333 Ok(Statement::DropSubscription(name))
334 }
335 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
336 self.advance();
337 let name = self.expect_ident_or_string()?;
338 Ok(Statement::DropUser(name))
339 }
340 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
342 self.advance();
343 let if_exists = self.consume_if_exists();
344 let name = self.expect_ident_like()?;
345 if !matches!(self.peek(), Token::On) {
347 return Err(self.err(alloc::format!(
348 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
349 self.peek()
350 )));
351 }
352 self.advance();
353 let table = self.expect_ident_like()?;
354 Ok(Statement::DropTrigger {
355 name,
356 table,
357 if_exists,
358 })
359 }
360 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
364 self.advance();
365 let if_exists = self.consume_if_exists();
366 let name = self.expect_ident_like()?;
367 if matches!(self.peek(), Token::LParen) {
369 self.advance();
370 let mut depth = 1usize;
372 while depth > 0 {
373 match self.peek() {
374 Token::LParen => depth += 1,
375 Token::RParen => depth -= 1,
376 Token::Eof => {
377 return Err(self.err(alloc::format!(
378 "unterminated arg list in DROP FUNCTION {name:?}"
379 )));
380 }
381 _ => {}
382 }
383 self.advance();
384 }
385 }
386 Ok(Statement::DropFunction { name, if_exists })
387 }
388 other => Err(self.err(format!(
389 "expected USER / PUBLICATION / SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
390 ))),
391 }
392 }
393 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
394 self.advance();
395 self.parse_update_after_keyword()
396 }
397 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
398 self.advance();
399 self.parse_delete_after_keyword()
400 }
401 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
405 self.advance();
406 self.parse_alter_after_keyword()
407 }
408 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
412 self.advance();
413 self.parse_wait_after_keyword()
414 }
415 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
426 self.advance();
427 let next = self.peek().clone();
428 let cold = match next {
429 Token::Ident(s) | Token::QuotedIdent(s) => s,
430 _ => {
431 return Err(
432 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
433 );
434 }
435 };
436 if !cold.eq_ignore_ascii_case("cold") {
437 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
438 }
439 self.advance();
440 let next = self.peek().clone();
441 let segments = match next {
442 Token::Ident(s) | Token::QuotedIdent(s) => s,
443 _ => {
444 return Err(self.err(format!(
445 "expected SEGMENTS after COMPACT COLD, got {:?}",
446 self.peek()
447 )));
448 }
449 };
450 if !segments.eq_ignore_ascii_case("segments") {
451 return Err(self.err(format!(
452 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
453 )));
454 }
455 self.advance();
456 Ok(Statement::CompactColdSegments)
457 }
458 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
459 self.advance();
460 let target = match self.peek() {
461 Token::Eof | Token::Semicolon => None,
462 Token::Ident(_) | Token::QuotedIdent(_) => {
463 Some(self.expect_ident_like()?)
464 }
465 other => {
466 return Err(self.err(format!(
467 "expected table name or end of statement after ANALYZE, got {other:?}"
468 )));
469 }
470 };
471 Ok(Statement::Analyze(target))
472 }
473 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
479 self.advance();
480 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("local") || s.eq_ignore_ascii_case("session"))
483 {
484 self.advance();
485 }
486 let name = self.parse_set_param_name()?;
487 match self.peek() {
489 Token::Eq => {
490 self.advance();
491 }
492 Token::To => {
493 self.advance();
494 }
495 other => {
496 return Err(self.err(format!(
497 "expected `=` or TO after SET {name}, got {other:?}"
498 )));
499 }
500 }
501 let value = self.parse_set_value()?;
502 Ok(Statement::SetParameter { name, value })
503 }
504 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
506 self.advance();
507 match self.peek().clone() {
508 Token::All => {
509 self.advance();
510 Ok(Statement::ResetParameter(None))
511 }
512 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
513 self.advance();
514 Ok(Statement::ResetParameter(None))
515 }
516 _ => {
517 let name = self.parse_set_param_name()?;
518 Ok(Statement::ResetParameter(Some(name)))
519 }
520 }
521 }
522 other => Err(self.err(format!(
523 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
524 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
525 ))),
526 }
527 }
528
529 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
530 debug_assert!(matches!(self.peek(), Token::Create));
531 self.advance();
532 match self.peek() {
533 Token::Table => self.parse_create_table_stmt_after_create(),
534 Token::Index => self.parse_create_index_stmt_after_create(false),
535 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
542 self.advance();
543 if !matches!(self.peek(), Token::Index) {
544 return Err(self.err(alloc::format!(
545 "expected INDEX after CREATE UNIQUE, got {:?}",
546 self.peek()
547 )));
548 }
549 self.parse_create_index_stmt_after_create(true)
550 }
551 Token::Publication => {
552 self.advance();
553 self.parse_create_publication_after_keyword()
554 }
555 Token::Subscription => {
556 self.advance();
557 self.parse_create_subscription_after_keyword()
558 }
559 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
563 self.advance();
564 self.parse_create_user_after_keyword()
565 }
566 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
570 self.advance();
571 self.parse_create_extension_after_keyword()
572 }
573 Token::Or => {
579 self.advance();
580 let next = self.peek();
581 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
582 return Err(self.err(alloc::format!(
583 "expected REPLACE after CREATE OR, got {next:?}"
584 )));
585 };
586 if !s2.eq_ignore_ascii_case("replace") {
587 return Err(self.err(alloc::format!(
588 "expected REPLACE after CREATE OR, got {s2:?}"
589 )));
590 }
591 self.advance();
592 self.parse_create_function_or_trigger_after_or_replace(true)
593 }
594 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
595 self.advance();
596 self.parse_create_function_after_keyword(false)
597 }
598 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
599 self.advance();
600 self.parse_create_trigger_after_keyword(false)
601 }
602 other => Err(self.err(format!(
603 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER [OR REPLACE …] after CREATE, got {other:?}"
604 ))),
605 }
606 }
607
608 fn parse_create_function_or_trigger_after_or_replace(
613 &mut self,
614 or_replace: bool,
615 ) -> Result<Statement, ParseError> {
616 let tok = self.peek();
617 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
618 return Err(self.err(alloc::format!(
619 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {tok:?}"
620 )));
621 };
622 if s.eq_ignore_ascii_case("function") {
623 self.advance();
624 self.parse_create_function_after_keyword(or_replace)
625 } else if s.eq_ignore_ascii_case("trigger") {
626 self.advance();
627 self.parse_create_trigger_after_keyword(or_replace)
628 } else {
629 Err(self.err(alloc::format!(
630 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {s:?}"
631 )))
632 }
633 }
634
635 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
640 self.consume_if_not_exists();
642 let name = self.expect_ident_like()?;
643 loop {
646 match self.peek() {
647 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
648 self.advance();
649 continue;
650 }
651 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
652 self.advance();
653 let _ = self.expect_ident_like()?;
654 continue;
655 }
656 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
657 self.advance();
658 let _ = self.advance();
660 continue;
661 }
662 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
663 self.advance();
664 let _ = self.advance();
665 continue;
666 }
667 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
668 self.advance();
669 continue;
670 }
671 _ => break,
672 }
673 }
674 Ok(Statement::CreateExtension(name))
675 }
676
677 fn parse_create_function_after_keyword(
689 &mut self,
690 or_replace: bool,
691 ) -> Result<Statement, ParseError> {
692 let name = self.expect_ident_like()?;
693 if !matches!(self.peek(), Token::LParen) {
697 return Err(self.err(alloc::format!(
698 "expected '(' after function name {name:?}, got {:?}",
699 self.peek()
700 )));
701 }
702 self.advance();
703 let args = self.parse_function_arg_list()?;
704 let tok = self.peek();
706 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
707 return Err(self.err(alloc::format!(
708 "expected RETURNS after function arg list, got {tok:?}"
709 )));
710 };
711 if !s.eq_ignore_ascii_case("returns") {
712 return Err(self.err(alloc::format!(
713 "expected RETURNS after function arg list, got {s:?}"
714 )));
715 }
716 self.advance();
717 let returns = self.parse_function_return()?;
718 let mut language: Option<String> = self.parse_optional_language()?;
721 if !matches!(self.peek(), Token::As) {
725 return Err(self.err(alloc::format!(
726 "expected AS before function body, got {:?}",
727 self.peek()
728 )));
729 }
730 self.advance();
731 let body_text = match self.peek() {
732 Token::String(s) => {
733 let body = s.clone();
734 self.advance();
735 body
736 }
737 other => {
738 return Err(self.err(alloc::format!(
739 "expected $$-quoted function body after AS, got {other:?}"
740 )));
741 }
742 };
743 if language.is_none() {
745 language = self.parse_optional_language()?;
746 }
747 let language = language.unwrap_or_else(|| String::from("sql"));
748 let body = if language.eq_ignore_ascii_case("plpgsql") {
753 match parse_plpgsql_body(&body_text) {
754 Ok(block) => FunctionBody::PlPgSql(block),
755 Err(_) => FunctionBody::Raw(body_text),
761 }
762 } else {
763 FunctionBody::Raw(body_text)
764 };
765 Ok(Statement::CreateFunction(CreateFunctionStatement {
766 name,
767 or_replace,
768 args,
769 returns,
770 language,
771 body,
772 }))
773 }
774
775 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
779 let mut args: Vec<FunctionArg> = Vec::new();
780 if matches!(self.peek(), Token::RParen) {
781 self.advance();
782 return Ok(args);
783 }
784 loop {
785 let mode = if matches!(self.peek(), Token::In) {
788 self.advance();
789 FunctionArgMode::In
790 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
791 {
792 self.advance();
793 FunctionArgMode::Out
794 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
795 {
796 self.advance();
797 FunctionArgMode::InOut
798 } else {
799 FunctionArgMode::In
800 };
801 let (name, ty_token) = {
807 let first = self.expect_ident_like()?;
808 match self.peek() {
811 Token::Ident(_) | Token::QuotedIdent(_) => {
812 let ty = self.expect_ident_like()?;
813 (Some(first), ty)
814 }
815 _ => (None, first),
816 }
817 };
818 let ty = match map_type_ident_to_column_type_name(&ty_token) {
820 Some(t) => FunctionArgType::Typed(t),
821 None => FunctionArgType::Raw(ty_token),
822 };
823 args.push(FunctionArg { mode, name, ty });
824 match self.peek() {
825 Token::Comma => {
826 self.advance();
827 continue;
828 }
829 Token::RParen => {
830 self.advance();
831 return Ok(args);
832 }
833 other => {
834 return Err(self.err(alloc::format!(
835 "expected , or ) in function arg list, got {other:?}"
836 )));
837 }
838 }
839 }
840 }
841
842 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
843 let ident = self.expect_ident_like()?;
844 if ident.eq_ignore_ascii_case("trigger") {
845 return Ok(FunctionReturn::Trigger);
846 }
847 if ident.eq_ignore_ascii_case("void") {
848 return Ok(FunctionReturn::Void);
849 }
850 match map_type_ident_to_column_type_name(&ident) {
851 Some(t) => Ok(FunctionReturn::Type(t)),
852 None => Ok(FunctionReturn::Other(ident)),
853 }
854 }
855
856 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
857 match self.peek() {
858 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
859 self.advance();
860 let lang = self.expect_ident_like()?;
861 Ok(Some(lang.to_ascii_lowercase()))
862 }
863 _ => Ok(None),
864 }
865 }
866
867 fn parse_create_trigger_after_keyword(
871 &mut self,
872 or_replace: bool,
873 ) -> Result<Statement, ParseError> {
874 let name = self.expect_ident_like()?;
875 let timing = {
876 let ident = self.expect_ident_like()?;
877 if ident.eq_ignore_ascii_case("before") {
878 TriggerTiming::Before
879 } else if ident.eq_ignore_ascii_case("after") {
880 TriggerTiming::After
881 } else if ident.eq_ignore_ascii_case("instead") {
882 let next = self.expect_ident_like()?;
883 if !next.eq_ignore_ascii_case("of") {
884 return Err(self.err(alloc::format!(
885 "expected OF after INSTEAD in trigger timing, got {next:?}"
886 )));
887 }
888 TriggerTiming::InsteadOf
889 } else {
890 return Err(self.err(alloc::format!(
891 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
892 )));
893 }
894 };
895 let mut events: Vec<TriggerEvent> = Vec::new();
898 events.push(self.parse_trigger_event()?);
899 while matches!(self.peek(), Token::Or) {
900 self.advance();
901 events.push(self.parse_trigger_event()?);
902 }
903 let tok = self.peek();
905 let Token::On = tok else {
906 return Err(self.err(alloc::format!(
907 "expected ON after trigger events, got {tok:?}"
908 )));
909 };
910 self.advance();
911 let table = self.expect_ident_like()?;
912 if !matches!(self.peek(), Token::For) {
916 return Err(self.err(alloc::format!(
917 "expected FOR EACH ROW / STATEMENT, got {:?}",
918 self.peek()
919 )));
920 }
921 self.advance();
922 let for_each = {
923 let e = self.expect_ident_like()?;
924 if !e.eq_ignore_ascii_case("each") {
925 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
926 }
927 let unit = self.expect_ident_like()?;
928 if unit.eq_ignore_ascii_case("row") {
929 TriggerForEach::Row
930 } else if unit.eq_ignore_ascii_case("statement") {
931 TriggerForEach::Statement
932 } else {
933 return Err(self.err(alloc::format!(
934 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
935 )));
936 }
937 };
938 let exec = self.expect_ident_like()?;
940 if !exec.eq_ignore_ascii_case("execute") {
941 return Err(self.err(alloc::format!(
942 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
943 )));
944 }
945 let fn_or_proc = self.expect_ident_like()?;
946 if !(fn_or_proc.eq_ignore_ascii_case("function")
947 || fn_or_proc.eq_ignore_ascii_case("procedure"))
948 {
949 return Err(self.err(alloc::format!(
950 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
951 )));
952 }
953 let function = self.expect_ident_like()?;
954 if matches!(self.peek(), Token::LParen) {
956 self.advance();
957 if !matches!(self.peek(), Token::RParen) {
958 return Err(self.err(alloc::format!(
959 "v7.12.4 trigger function calls take no args; got {:?}",
960 self.peek()
961 )));
962 }
963 self.advance();
964 }
965 Ok(Statement::CreateTrigger(CreateTriggerStatement {
966 name,
967 or_replace,
968 timing,
969 events,
970 table,
971 for_each,
972 function,
973 }))
974 }
975
976 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
983 let declarations = if matches!(
985 self.peek(),
986 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
987 ) {
988 self.advance();
989 self.parse_plpgsql_declare_block()?
990 } else {
991 Vec::new()
992 };
993 if !matches!(self.peek(), Token::Begin) {
998 return Err(self.err(alloc::format!(
999 "expected BEGIN at start of plpgsql block, got {:?}",
1000 self.peek()
1001 )));
1002 }
1003 self.advance();
1004 let statements = self.parse_plpgsql_stmt_list_until_end()?;
1005 Ok(PlPgSqlBlock {
1006 declarations,
1007 statements,
1008 })
1009 }
1010
1011 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
1015 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
1016 loop {
1017 if matches!(self.peek(), Token::Begin) {
1018 return Ok(out);
1019 }
1020 let name = self.expect_ident_like()?;
1021 let ty_token = self.expect_ident_like()?;
1022 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1023 Some(t) => FunctionArgType::Typed(t),
1024 None => FunctionArgType::Raw(ty_token),
1025 };
1026 let default = match self.peek() {
1027 Token::ColonEq => {
1028 self.advance();
1029 Some(self.parse_expr(0)?)
1030 }
1031 Token::Eq => {
1032 self.advance();
1036 Some(self.parse_expr(0)?)
1037 }
1038 _ => None,
1039 };
1040 if !matches!(self.peek(), Token::Semicolon) {
1042 return Err(self.err(alloc::format!(
1043 "expected ; after DECLARE entry for {name:?}, got {:?}",
1044 self.peek()
1045 )));
1046 }
1047 self.advance();
1048 out.push(PlPgSqlDeclare { name, ty, default });
1049 }
1050 }
1051
1052 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
1057 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
1058 loop {
1059 while matches!(self.peek(), Token::Semicolon) {
1061 self.advance();
1062 }
1063 if matches!(
1065 self.peek(),
1066 Token::Ident(s) | Token::QuotedIdent(s)
1067 if s.eq_ignore_ascii_case("end")
1068 || s.eq_ignore_ascii_case("else")
1069 || s.eq_ignore_ascii_case("elsif")
1070 || s.eq_ignore_ascii_case("elseif")
1071 ) {
1072 return Ok(statements);
1073 }
1074 let stmt = self.parse_plpgsql_stmt()?;
1077 statements.push(stmt);
1078 match self.peek() {
1079 Token::Semicolon => {
1080 self.advance();
1081 }
1082 Token::Ident(s) | Token::QuotedIdent(s)
1083 if s.eq_ignore_ascii_case("end")
1084 || s.eq_ignore_ascii_case("else")
1085 || s.eq_ignore_ascii_case("elsif")
1086 || s.eq_ignore_ascii_case("elseif") =>
1087 {
1088 }
1090 other => {
1091 return Err(self.err(alloc::format!(
1092 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
1093 )));
1094 }
1095 }
1096 }
1097 }
1098
1099 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1100 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
1102 {
1103 self.advance();
1104 return self.parse_plpgsql_return();
1105 }
1106 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
1108 {
1109 self.advance();
1110 return self.parse_plpgsql_if();
1111 }
1112 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
1114 {
1115 self.advance();
1116 return self.parse_plpgsql_raise();
1117 }
1118 if matches!(self.peek(), Token::Insert)
1124 || matches!(self.peek(), Token::Select)
1125 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("delete"))
1126 {
1127 let stmt = self.parse_one_statement()?;
1128 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
1129 }
1130 let target = self.parse_plpgsql_assign_target()?;
1133 match self.peek() {
1136 Token::ColonEq => {
1137 self.advance();
1138 }
1139 Token::Colon => {
1140 self.advance();
1141 if !matches!(self.peek(), Token::Eq) {
1142 return Err(self.err(alloc::format!(
1143 "expected := after plpgsql assign target, got `:` then {:?}",
1144 self.peek()
1145 )));
1146 }
1147 self.advance();
1148 }
1149 other => {
1150 return Err(self.err(alloc::format!(
1151 "expected := after plpgsql assign target, got {other:?}"
1152 )));
1153 }
1154 }
1155 let value = self.parse_expr(0)?;
1156 Ok(PlPgSqlStmt::Assign { target, value })
1157 }
1158
1159 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1162 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
1163 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
1164 loop {
1165 let cond = self.parse_expr(0)?;
1167 let then_kw = self.expect_ident_like()?;
1168 if !then_kw.eq_ignore_ascii_case("then") {
1169 return Err(self.err(alloc::format!(
1170 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
1171 )));
1172 }
1173 let body = self.parse_plpgsql_stmt_list_until_end()?;
1174 branches.push((cond, body));
1175 match self.peek() {
1177 Token::Ident(s) | Token::QuotedIdent(s)
1178 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
1179 {
1180 self.advance();
1181 continue;
1182 }
1183 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
1184 self.advance();
1185 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
1186 break;
1187 }
1188 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
1189 break;
1190 }
1191 other => {
1192 return Err(self.err(alloc::format!(
1193 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
1194 )));
1195 }
1196 }
1197 }
1198 let end_kw = self.expect_ident_like()?;
1201 if !end_kw.eq_ignore_ascii_case("end") {
1202 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
1203 }
1204 let if_kw = self.expect_ident_like()?;
1205 if !if_kw.eq_ignore_ascii_case("if") {
1206 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
1207 }
1208 Ok(PlPgSqlStmt::If {
1209 branches,
1210 else_branch,
1211 })
1212 }
1213
1214 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1218 let lvl_ident = self.expect_ident_like()?;
1219 let level = match lvl_ident.to_ascii_lowercase().as_str() {
1220 "notice" => RaiseLevel::Notice,
1221 "warning" => RaiseLevel::Warning,
1222 "info" => RaiseLevel::Info,
1223 "log" => RaiseLevel::Log,
1224 "debug" => RaiseLevel::Debug,
1225 "exception" => RaiseLevel::Exception,
1226 other => {
1227 return Err(self.err(alloc::format!(
1228 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
1229 )));
1230 }
1231 };
1232 let Token::String(msg) = self.peek() else {
1236 return Err(self.err(alloc::format!(
1237 "expected RAISE message string, got {:?}",
1238 self.peek()
1239 )));
1240 };
1241 let message = msg.clone();
1242 self.advance();
1243 let mut args: Vec<Expr> = Vec::new();
1245 while matches!(self.peek(), Token::Comma) {
1246 self.advance();
1247 args.push(self.parse_expr(0)?);
1248 }
1249 Ok(PlPgSqlStmt::Raise {
1250 level,
1251 message,
1252 args,
1253 })
1254 }
1255
1256 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
1257 let head = self.expect_ident_like()?;
1258 if matches!(self.peek(), Token::Dot) {
1259 self.advance();
1260 let col = self.expect_ident_like()?;
1261 if head.eq_ignore_ascii_case("new") {
1262 return Ok(AssignTarget::NewColumn(col));
1263 }
1264 if head.eq_ignore_ascii_case("old") {
1265 return Ok(AssignTarget::OldColumn(col));
1266 }
1267 return Err(self.err(alloc::format!(
1268 "v7.12.4 plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
1269 got {head:?}.<col>"
1270 )));
1271 }
1272 Ok(AssignTarget::Local(head))
1273 }
1274
1275 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1276 match self.peek() {
1278 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
1279 self.advance();
1280 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
1281 }
1282 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
1283 self.advance();
1284 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
1285 }
1286 Token::Null => {
1287 self.advance();
1288 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1289 }
1290 Token::Semicolon => {
1293 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1294 }
1295 _ => {}
1296 }
1297 let e = self.parse_expr(0)?;
1299 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
1300 }
1301
1302 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
1303 if matches!(self.peek(), Token::Insert) {
1308 self.advance();
1309 return Ok(TriggerEvent::Insert);
1310 }
1311 match self.peek() {
1312 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1313 self.advance();
1314 Ok(TriggerEvent::Update)
1315 }
1316 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1317 self.advance();
1318 Ok(TriggerEvent::Delete)
1319 }
1320 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
1321 self.advance();
1322 Ok(TriggerEvent::Truncate)
1323 }
1324 other => Err(self.err(alloc::format!(
1325 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
1326 ))),
1327 }
1328 }
1329
1330 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
1337 let name = self.expect_ident_or_string()?;
1338 let scope = if matches!(self.peek(), Token::For) {
1341 self.advance();
1342 if matches!(self.peek(), Token::All) {
1343 self.advance();
1344 if !matches!(self.peek(), Token::Tables) {
1345 return Err(self.err(format!(
1346 "expected TABLES after FOR ALL, got {:?}",
1347 self.peek()
1348 )));
1349 }
1350 self.advance();
1351 if matches!(self.peek(), Token::Except) {
1352 self.advance();
1353 let tables = self.parse_publication_table_list()?;
1354 PublicationScope::AllTablesExcept(tables)
1355 } else {
1356 PublicationScope::AllTables
1357 }
1358 } else if matches!(self.peek(), Token::Table | Token::Tables) {
1359 self.advance();
1362 let tables = self.parse_publication_table_list()?;
1363 PublicationScope::ForTables(tables)
1364 } else {
1365 return Err(self.err(format!(
1366 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
1367 self.peek()
1368 )));
1369 }
1370 } else {
1371 PublicationScope::AllTables
1372 };
1373 Ok(Statement::CreatePublication(CreatePublicationStatement {
1374 name,
1375 scope,
1376 }))
1377 }
1378
1379 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
1384 let first = self.expect_ident_like()?;
1385 let mut out = alloc::vec![first];
1386 while matches!(self.peek(), Token::Comma) {
1387 self.advance();
1388 out.push(self.expect_ident_like()?);
1389 }
1390 Ok(out)
1391 }
1392
1393 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
1401 let name = self.expect_ident_or_string()?;
1402 if !matches!(self.peek(), Token::Connection) {
1403 return Err(self.err(format!(
1404 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
1405 self.peek()
1406 )));
1407 }
1408 self.advance();
1409 let conn_str = self.expect_string_literal()?;
1410 if !matches!(self.peek(), Token::Publication) {
1411 return Err(self.err(format!(
1412 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
1413 self.peek()
1414 )));
1415 }
1416 self.advance();
1417 let first = self.expect_ident_like()?;
1420 let mut publications = alloc::vec![first];
1421 while matches!(self.peek(), Token::Comma) {
1422 self.advance();
1423 publications.push(self.expect_ident_like()?);
1424 }
1425 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
1426 name,
1427 conn_str,
1428 publications,
1429 }))
1430 }
1431
1432 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
1439 let mut name = self.expect_ident_like()?;
1440 while matches!(self.peek(), Token::Dot) {
1441 self.advance();
1442 let next = self.expect_ident_like()?;
1443 name.push('.');
1444 name.push_str(&next);
1445 }
1446 Ok(name.to_ascii_lowercase())
1447 }
1448
1449 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
1450 match self.advance() {
1451 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
1452 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
1453 Ok(crate::ast::SetValue::Default)
1454 }
1455 Token::Ident(s) | Token::QuotedIdent(s) => {
1456 let mut accum = s;
1457 while matches!(self.peek(), Token::Dot) {
1458 self.advance();
1459 let next = self.expect_ident_like()?;
1460 accum.push('.');
1461 accum.push_str(&next);
1462 }
1463 Ok(crate::ast::SetValue::Ident(accum))
1464 }
1465 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
1466 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
1467 other => Err(self.err(format!(
1468 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
1469 ))),
1470 }
1471 }
1472
1473 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
1474 if !matches!(self.peek(), Token::For) {
1478 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
1479 }
1480 self.advance();
1481 self.expect_keyword_ident("wal")?;
1482 self.expect_keyword_ident("position")?;
1483 let pos = self.expect_u64_literal()?;
1484 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
1485 {
1486 self.advance();
1487 self.expect_keyword_ident("timeout")?;
1488 Some(self.expect_u64_literal()?)
1489 } else {
1490 None
1491 };
1492 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
1493 }
1494
1495 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
1499 match self.advance() {
1500 Token::Integer(n) if n >= 0 => Ok(n as u64),
1501 Token::Integer(n) => Err(ParseError {
1502 message: format!("expected non-negative integer, got {n}"),
1503 token_pos: self.pos.saturating_sub(1),
1504 }),
1505 other => Err(ParseError {
1506 message: format!("expected integer literal, got {other:?}"),
1507 token_pos: self.pos.saturating_sub(1),
1508 }),
1509 }
1510 }
1511
1512 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
1516 let name = self.expect_ident_or_string()?;
1517 self.expect_keyword_ident("with")?;
1518 self.expect_keyword_ident("password")?;
1519 let password = self.expect_string_literal()?;
1520 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
1521 && s.eq_ignore_ascii_case("role")
1522 {
1523 self.advance();
1524 self.expect_string_literal()?
1525 } else {
1526 "readonly".to_string()
1527 };
1528 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
1529 name,
1530 password,
1531 role,
1532 }))
1533 }
1534
1535 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
1538 let table = self.expect_ident_like()?;
1539 self.expect_keyword_ident("set")?;
1540 let mut assignments = Vec::new();
1541 loop {
1542 let col = self.expect_ident_like()?;
1543 if !matches!(self.peek(), Token::Eq) {
1544 return Err(self.err(format!(
1545 "expected `=` after column name in UPDATE SET, got {:?}",
1546 self.peek()
1547 )));
1548 }
1549 self.advance();
1550 let value = self.parse_expr(0)?;
1551 assignments.push((col, value));
1552 if matches!(self.peek(), Token::Comma) {
1553 self.advance();
1554 continue;
1555 }
1556 break;
1557 }
1558 let where_ = if matches!(self.peek(), Token::Where) {
1559 self.advance();
1560 Some(self.parse_expr(0)?)
1561 } else {
1562 None
1563 };
1564 let returning = self.parse_optional_returning()?;
1565 Ok(Statement::Update(crate::ast::UpdateStatement {
1566 table,
1567 assignments,
1568 where_,
1569 returning,
1570 }))
1571 }
1572
1573 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
1576 if !matches!(self.peek(), Token::From) {
1577 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
1578 }
1579 self.advance();
1580 let table = self.expect_ident_like()?;
1581 let where_ = if matches!(self.peek(), Token::Where) {
1582 self.advance();
1583 Some(self.parse_expr(0)?)
1584 } else {
1585 None
1586 };
1587 let returning = self.parse_optional_returning()?;
1588 Ok(Statement::Delete(crate::ast::DeleteStatement {
1589 table,
1590 where_,
1591 returning,
1592 }))
1593 }
1594
1595 fn parse_optional_returning(
1600 &mut self,
1601 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
1602 let is_returning_kw = matches!(
1603 self.peek(),
1604 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
1605 );
1606 if !is_returning_kw {
1607 return Ok(None);
1608 }
1609 self.advance();
1610 let mut items = Vec::new();
1611 loop {
1612 items.push(self.parse_select_item()?);
1613 if matches!(self.peek(), Token::Comma) {
1614 self.advance();
1615 continue;
1616 }
1617 break;
1618 }
1619 Ok(Some(items))
1620 }
1621
1622 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
1630 match self.advance() {
1632 Token::Index => {}
1633 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
1634 Token::Table => return self.parse_alter_table_after_keyword(),
1636 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
1637 return self.parse_alter_table_after_keyword();
1638 }
1639 other => {
1640 return Err(self.err(format!(
1641 "expected INDEX or TABLE after ALTER, got {other:?}"
1642 )));
1643 }
1644 }
1645 let name = self.expect_ident_like()?;
1646 self.expect_keyword_ident("rebuild")?;
1648 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
1650 self.advance();
1651 if !matches!(self.peek(), Token::LParen) {
1652 return Err(self.err(format!(
1653 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
1654 self.peek()
1655 )));
1656 }
1657 self.advance();
1658 self.expect_keyword_ident("encoding")?;
1659 if !matches!(self.peek(), Token::Eq) {
1660 return Err(self.err(format!(
1661 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
1662 self.peek()
1663 )));
1664 }
1665 self.advance();
1666 let enc_ident = match self.advance() {
1667 Token::Ident(s) | Token::QuotedIdent(s) => s,
1668 other => {
1669 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
1670 }
1671 };
1672 let enc = match enc_ident.to_ascii_lowercase().as_str() {
1673 "f32" => VecEncoding::F32,
1674 "sq8" => VecEncoding::Sq8,
1675 "half" => VecEncoding::F16,
1676 other => {
1677 return Err(self.err(format!(
1678 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
1679 )));
1680 }
1681 };
1682 if !matches!(self.peek(), Token::RParen) {
1683 return Err(self.err(format!(
1684 "expected ')' after encoding value, got {:?}",
1685 self.peek()
1686 )));
1687 }
1688 self.advance();
1689 Some(enc)
1690 } else {
1691 None
1692 };
1693 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
1694 name,
1695 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
1696 }))
1697 }
1698
1699 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
1703 let table_name = self.expect_ident_like()?;
1704 match self.peek() {
1708 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
1709 self.advance();
1710 let setting = self.expect_ident_like()?;
1711 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
1712 return Err(self.err(alloc::format!(
1713 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
1714 )));
1715 }
1716 if !matches!(self.peek(), Token::Eq) {
1717 return Err(self.err(alloc::format!(
1718 "expected '=' after hot_tier_bytes, got {:?}",
1719 self.peek()
1720 )));
1721 }
1722 self.advance();
1723 let n = self.expect_u64_literal()?;
1724 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1725 name: table_name,
1726 target: crate::ast::AlterTableTarget::SetHotTierBytes(n),
1727 }))
1728 }
1729 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
1730 self.advance();
1731 let fk = self.parse_table_level_fk()?;
1734 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1735 name: table_name,
1736 target: crate::ast::AlterTableTarget::AddForeignKey(fk),
1737 }))
1738 }
1739 Token::Drop => {
1740 self.advance();
1741 match self.advance() {
1742 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
1743 other => {
1744 return Err(self.err(alloc::format!(
1745 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
1746 )));
1747 }
1748 }
1749 let cname = self.expect_ident_like()?;
1750 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1751 name: table_name,
1752 target: crate::ast::AlterTableTarget::DropForeignKey(cname),
1753 }))
1754 }
1755 other => Err(self.err(alloc::format!(
1756 "expected SET / ADD / DROP in ALTER TABLE, got {other:?}"
1757 ))),
1758 }
1759 }
1760
1761 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
1763 match self.advance() {
1764 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
1765 other => Err(ParseError {
1766 message: format!("expected {kw:?}, got {other:?}"),
1767 token_pos: self.pos.saturating_sub(1),
1768 }),
1769 }
1770 }
1771
1772 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
1776 match self.advance() {
1777 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
1778 other => Err(ParseError {
1779 message: format!("expected identifier or string, got {other:?}"),
1780 token_pos: self.pos.saturating_sub(1),
1781 }),
1782 }
1783 }
1784
1785 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
1786 match self.advance() {
1787 Token::String(s) => Ok(s),
1788 other => Err(ParseError {
1789 message: format!("expected quoted string, got {other:?}"),
1790 token_pos: self.pos.saturating_sub(1),
1791 }),
1792 }
1793 }
1794
1795 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
1796 let mut head = self.parse_bare_select()?;
1801 while matches!(self.peek(), Token::Union) {
1802 self.advance();
1803 let kind = if matches!(self.peek(), Token::All) {
1804 self.advance();
1805 UnionKind::All
1806 } else {
1807 UnionKind::Distinct
1808 };
1809 let peer = self.parse_bare_select()?;
1810 head.unions.push((kind, peer));
1811 }
1812 head.order_by = if matches!(self.peek(), Token::Order) {
1813 self.advance();
1814 if !matches!(self.peek(), Token::By) {
1815 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
1816 }
1817 self.advance();
1818 let mut keys = Vec::new();
1821 loop {
1822 let expr = self.parse_expr(0)?;
1823 let desc = if matches!(self.peek(), Token::Desc) {
1824 self.advance();
1825 true
1826 } else if matches!(self.peek(), Token::Asc) {
1827 self.advance();
1828 false
1829 } else {
1830 false
1831 };
1832 keys.push(OrderBy { expr, desc });
1833 if matches!(self.peek(), Token::Comma) {
1834 self.advance();
1835 } else {
1836 break;
1837 }
1838 }
1839 keys
1840 } else {
1841 Vec::new()
1842 };
1843 head.limit = if matches!(self.peek(), Token::Limit) {
1844 self.advance();
1845 Some(self.parse_limit_expr("LIMIT")?)
1846 } else {
1847 None
1848 };
1849 head.offset = if matches!(self.peek(), Token::Offset) {
1850 self.advance();
1851 Some(self.parse_limit_expr("OFFSET")?)
1852 } else {
1853 None
1854 };
1855 Ok(Statement::Select(head))
1856 }
1857
1858 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
1863 match self.advance() {
1864 Token::Integer(n) if n >= 0 => u32::try_from(n)
1865 .map(crate::ast::LimitExpr::Literal)
1866 .map_err(|_| ParseError {
1867 message: alloc::format!("{label} value too large: {n}"),
1868 token_pos: self.pos.saturating_sub(1),
1869 }),
1870 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
1871 other => Err(ParseError {
1872 message: alloc::format!(
1873 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
1874 ),
1875 token_pos: self.pos.saturating_sub(1),
1876 }),
1877 }
1878 }
1879
1880 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
1885 if !matches!(self.peek(), Token::Select) {
1886 return Err(self.err(format!(
1887 "expected SELECT to start a query block, got {:?}",
1888 self.peek()
1889 )));
1890 }
1891 self.advance();
1892 let distinct = if matches!(self.peek(), Token::Distinct) {
1893 self.advance();
1894 true
1895 } else {
1896 false
1897 };
1898 let items = self.parse_select_list()?;
1899 let from = if matches!(self.peek(), Token::From) {
1900 self.advance();
1901 Some(self.parse_from_clause()?)
1902 } else {
1903 None
1904 };
1905 let where_ = if matches!(self.peek(), Token::Where) {
1906 self.advance();
1907 Some(self.parse_expr(0)?)
1908 } else {
1909 None
1910 };
1911 let mut group_by_all = false;
1912 let group_by = if matches!(self.peek(), Token::Group) {
1913 self.advance();
1914 if !matches!(self.peek(), Token::By) {
1915 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
1916 }
1917 self.advance();
1918 if matches!(self.peek(), Token::All) {
1921 self.advance();
1922 group_by_all = true;
1923 None
1924 } else {
1925 let mut groups = Vec::new();
1926 loop {
1927 groups.push(self.parse_expr(0)?);
1928 if matches!(self.peek(), Token::Comma) {
1929 self.advance();
1930 } else {
1931 break;
1932 }
1933 }
1934 Some(groups)
1935 }
1936 } else {
1937 None
1938 };
1939 let having = if matches!(self.peek(), Token::Having) {
1940 self.advance();
1941 Some(self.parse_expr(0)?)
1942 } else {
1943 None
1944 };
1945 Ok(SelectStatement {
1946 ctes: Vec::new(),
1947 distinct,
1948 items,
1949 from,
1950 where_,
1951 group_by,
1952 group_by_all,
1953 having,
1954 unions: Vec::new(),
1955 order_by: Vec::new(),
1956 limit: None,
1957 offset: None,
1958 })
1959 }
1960
1961 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
1962 debug_assert!(matches!(self.peek(), Token::Table));
1964 self.advance();
1965 let if_not_exists = self.consume_if_not_exists();
1966 let name = self.expect_ident_like()?;
1967 if !matches!(self.peek(), Token::LParen) {
1968 return Err(self.err(format!(
1969 "expected '(' after table name, got {:?}",
1970 self.peek()
1971 )));
1972 }
1973 self.advance();
1974 let mut columns = Vec::new();
1975 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
1976 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
1977 loop {
1978 if self.peek_table_level_pk_start() {
1984 table_constraints.push(self.parse_table_level_primary_key()?);
1985 } else if self.peek_table_level_unique_start() {
1986 table_constraints.push(self.parse_table_level_unique()?);
1987 } else if self.peek_constraint_or_fk_start() {
1988 foreign_keys.push(self.parse_table_level_fk()?);
1989 } else {
1990 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
1991 columns.push(col);
1992 if let Some(fk) = col_level_fk {
1993 foreign_keys.push(fk);
1994 }
1995 }
1996 match self.peek() {
1997 Token::Comma => {
1998 self.advance();
1999 }
2000 Token::RParen => {
2001 self.advance();
2002 break;
2003 }
2004 other => {
2005 return Err(
2006 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2007 );
2008 }
2009 }
2010 }
2011 if columns.is_empty() {
2012 return Err(self.err("CREATE TABLE requires at least one column".into()));
2013 }
2014 Ok(Statement::CreateTable(CreateTableStatement {
2015 name,
2016 columns,
2017 if_not_exists,
2018 foreign_keys,
2019 table_constraints,
2020 }))
2021 }
2022
2023 fn peek_table_level_pk_start(&self) -> bool {
2028 let cur = self.peek();
2029 let nxt = self.tokens.get(self.pos + 1);
2030 let nxt2 = self.tokens.get(self.pos + 2);
2031 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
2032 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
2033 let is_lparen = matches!(nxt2, Some(Token::LParen));
2034 is_primary && is_key && is_lparen
2035 }
2036
2037 fn peek_table_level_unique_start(&self) -> bool {
2039 let cur = self.peek();
2040 let nxt = self.tokens.get(self.pos + 1);
2041 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
2042 let is_lparen = matches!(nxt, Some(Token::LParen));
2043 is_unique && is_lparen
2044 }
2045
2046 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2047 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
2050 Ok(crate::ast::TableConstraint::PrimaryKey {
2051 name: None,
2052 columns,
2053 })
2054 }
2055
2056 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2057 self.advance(); let columns = self.parse_paren_ident_list("UNIQUE")?;
2059 Ok(crate::ast::TableConstraint::Unique {
2060 name: None,
2061 columns,
2062 })
2063 }
2064
2065 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
2066 if !matches!(self.peek(), Token::LParen) {
2067 return Err(self.err(alloc::format!(
2068 "expected '(' after {ctx}, got {:?}",
2069 self.peek()
2070 )));
2071 }
2072 self.advance();
2073 let mut out = Vec::new();
2074 loop {
2075 out.push(self.expect_ident_like()?);
2076 match self.peek() {
2077 Token::Comma => {
2078 self.advance();
2079 }
2080 Token::RParen => {
2081 self.advance();
2082 break;
2083 }
2084 other => {
2085 return Err(self.err(alloc::format!(
2086 "expected ',' or ')' in {ctx} list, got {other:?}"
2087 )));
2088 }
2089 }
2090 }
2091 if out.is_empty() {
2092 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
2093 }
2094 Ok(out)
2095 }
2096
2097 fn peek_constraint_or_fk_start(&self) -> bool {
2102 let is_constraint_kw = matches!(
2103 self.peek(),
2104 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2105 );
2106 let is_foreign_kw = matches!(
2107 self.peek(),
2108 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
2109 );
2110 is_constraint_kw || is_foreign_kw
2111 }
2112
2113 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
2117 let mut name: Option<String> = None;
2118 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
2119 self.advance();
2120 name = Some(self.expect_ident_like()?);
2121 }
2122 match self.advance() {
2124 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
2125 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
2126 }
2127 match self.advance() {
2129 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
2130 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
2131 }
2132 if !matches!(self.peek(), Token::LParen) {
2134 return Err(self.err(format!(
2135 "expected '(' after FOREIGN KEY, got {:?}",
2136 self.peek()
2137 )));
2138 }
2139 self.advance();
2140 let mut columns = Vec::new();
2141 loop {
2142 columns.push(self.expect_ident_like()?);
2143 match self.peek() {
2144 Token::Comma => {
2145 self.advance();
2146 }
2147 Token::RParen => {
2148 self.advance();
2149 break;
2150 }
2151 other => {
2152 return Err(self.err(format!(
2153 "expected ',' or ')' in FK column list, got {other:?}"
2154 )));
2155 }
2156 }
2157 }
2158 if columns.is_empty() {
2159 return Err(self.err("FOREIGN KEY requires at least one column".into()));
2160 }
2161 let (parent_table, parent_columns, on_delete, on_update) =
2162 self.parse_references_tail(columns.len())?;
2163 Ok(ForeignKeyConstraint {
2164 name,
2165 columns,
2166 parent_table,
2167 parent_columns,
2168 on_delete,
2169 on_update,
2170 })
2171 }
2172
2173 fn parse_references_tail(
2178 &mut self,
2179 expected_arity: usize,
2180 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
2181 match self.advance() {
2182 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
2183 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
2184 }
2185 let parent_table = self.expect_ident_like()?;
2186 let mut parent_columns: Vec<String> = Vec::new();
2187 if matches!(self.peek(), Token::LParen) {
2188 self.advance();
2189 loop {
2190 parent_columns.push(self.expect_ident_like()?);
2191 match self.peek() {
2192 Token::Comma => {
2193 self.advance();
2194 }
2195 Token::RParen => {
2196 self.advance();
2197 break;
2198 }
2199 other => {
2200 return Err(self.err(format!(
2201 "expected ',' or ')' in REFERENCES column list, got {other:?}"
2202 )));
2203 }
2204 }
2205 }
2206 }
2207 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
2208 return Err(self.err(format!(
2209 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
2210 expected_arity,
2211 parent_columns.len()
2212 )));
2213 }
2214 loop {
2220 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2221 return Err(self.err(
2222 "DEFERRABLE constraints are not supported (SPG is single-writer; \
2223 constraints are always evaluated immediately at commit)"
2224 .into(),
2225 ));
2226 }
2227 if matches!(self.peek(), Token::Not) {
2228 let look = self.tokens.get(self.pos + 1);
2229 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2230 self.advance();
2233 self.advance();
2234 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
2236 {
2237 self.advance();
2238 match self.advance() {
2239 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
2240 other => {
2241 return Err(self.err(format!(
2242 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
2243 got {other:?}"
2244 )));
2245 }
2246 }
2247 }
2248 continue;
2249 }
2250 break;
2251 }
2252 break;
2253 }
2254 let mut on_delete = FkAction::Restrict;
2257 let mut on_update = FkAction::Restrict;
2258 let mut seen_on_delete = false;
2259 let mut seen_on_update = false;
2260 loop {
2261 if !matches!(self.peek(), Token::On) {
2262 break;
2263 }
2264 self.advance();
2265 let which = self.advance();
2266 let action = self.parse_fk_action()?;
2267 match which {
2268 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
2269 if seen_on_delete {
2270 return Err(self.err("ON DELETE specified twice".into()));
2271 }
2272 seen_on_delete = true;
2273 on_delete = action;
2274 }
2275 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
2276 if seen_on_update {
2277 return Err(self.err("ON UPDATE specified twice".into()));
2278 }
2279 seen_on_update = true;
2280 on_update = action;
2281 }
2282 other => {
2283 return Err(
2284 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
2285 );
2286 }
2287 }
2288 }
2289 Ok((parent_table, parent_columns, on_delete, on_update))
2290 }
2291
2292 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
2295 match self.advance() {
2296 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
2297 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
2298 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
2299 Token::Null => Ok(FkAction::SetNull),
2300 Token::Default => Ok(FkAction::SetDefault),
2301 other => Err(self.err(format!(
2302 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
2303 ))),
2304 },
2305 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
2306 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
2307 other => Err(self.err(format!(
2308 "expected ACTION after NO in FK action, got {other:?}"
2309 ))),
2310 },
2311 other => Err(self.err(format!(
2312 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
2313 ))),
2314 }
2315 }
2316
2317 fn consume_if_not_exists(&mut self) -> bool {
2320 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2324 if !looks_like_if {
2325 return false;
2326 }
2327 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
2330 return false;
2331 }
2332 if !matches!(
2333 self.tokens.get(self.pos + 2),
2334 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2335 ) {
2336 return false;
2337 }
2338 self.advance(); self.advance(); self.advance(); true
2342 }
2343
2344 fn consume_if_exists(&mut self) -> bool {
2348 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2349 if !looks_like_if {
2350 return false;
2351 }
2352 if !matches!(
2353 self.tokens.get(self.pos + 1),
2354 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2355 ) {
2356 return false;
2357 }
2358 self.advance(); self.advance(); true
2361 }
2362
2363 fn consume_optional_index_column_qualifiers(&mut self) {
2369 loop {
2370 match self.peek() {
2371 Token::Asc | Token::Desc => {
2372 self.advance();
2373 }
2374 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
2375 let look = self.tokens.get(self.pos + 1);
2376 if matches!(
2377 look,
2378 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
2379 || k.eq_ignore_ascii_case("last")
2380 ) {
2381 self.advance();
2382 self.advance();
2383 } else {
2384 break;
2385 }
2386 }
2387 _ => break,
2388 }
2389 }
2390 }
2391
2392 fn parse_create_index_stmt_after_create(
2393 &mut self,
2394 is_unique: bool,
2395 ) -> Result<Statement, ParseError> {
2396 debug_assert!(matches!(self.peek(), Token::Index));
2398 self.advance();
2399 let if_not_exists = self.consume_if_not_exists();
2400 let name = self.expect_ident_like()?;
2401 if !matches!(self.peek(), Token::On) {
2402 return Err(self.err(format!(
2403 "expected ON after CREATE INDEX <name>, got {:?}",
2404 self.peek()
2405 )));
2406 }
2407 self.advance();
2408 let table = self.expect_ident_like()?;
2409 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2414 self.advance();
2415 let m = self.expect_ident_like()?;
2416 match m.to_ascii_lowercase().as_str() {
2417 "hnsw" => IndexMethod::Hnsw,
2418 "btree" => IndexMethod::BTree,
2419 "brin" => IndexMethod::Brin,
2420 "gin" => IndexMethod::Gin,
2425 "gist" | "spgist" | "hash" => IndexMethod::BTree,
2434 "ivfflat" => IndexMethod::Hnsw,
2442 other => {
2443 return Err(self.err(alloc::format!(
2444 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
2445 )));
2446 }
2447 }
2448 } else {
2449 IndexMethod::BTree
2450 };
2451 if !matches!(self.peek(), Token::LParen) {
2452 return Err(self.err(format!(
2453 "expected '(' before indexed column, got {:?}",
2454 self.peek()
2455 )));
2456 }
2457 self.advance();
2458 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
2467 Token::Ident(s) | Token::QuotedIdent(s)
2474 if matches!(
2475 self.tokens.get(self.pos + 1),
2476 Some(Token::RParen | Token::Comma)
2477 ) =>
2478 {
2479 self.advance();
2480 (s, None)
2481 }
2482 Token::Ident(s) | Token::QuotedIdent(s)
2490 if matches!(
2491 self.tokens.get(self.pos + 1),
2492 Some(Token::Ident(op) | Token::QuotedIdent(op))
2493 if is_vector_opclass_name(op)
2494 ) =>
2495 {
2496 self.advance(); self.advance(); (s, None)
2499 }
2500 Token::Ident(_) | Token::QuotedIdent(_) => {
2501 let key_expr = self.parse_expr(0)?;
2502 let primary = extract_first_column(&key_expr).ok_or_else(|| {
2503 self.err("expression index key must reference at least one column".into())
2504 })?;
2505 (primary, Some(key_expr))
2506 }
2507 other => {
2508 return Err(self.err(format!(
2509 "expected column ident or expression, got {other:?}"
2510 )));
2511 }
2512 };
2513 let mut extra_columns: Vec<String> = Vec::new();
2522 self.consume_optional_index_column_qualifiers();
2524 while matches!(self.peek(), Token::Comma) {
2525 self.advance();
2526 let extra = self.expect_ident_like()?;
2527 self.consume_optional_index_column_qualifiers();
2528 extra_columns.push(extra);
2529 }
2530 if !matches!(self.peek(), Token::RParen) {
2531 return Err(self.err(format!(
2532 "expected ')' after indexed column / expression, got {:?}",
2533 self.peek()
2534 )));
2535 }
2536 self.advance();
2537 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
2541 {
2542 self.advance();
2543 if !matches!(self.peek(), Token::LParen) {
2544 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
2545 }
2546 self.advance();
2547 let mut cols = Vec::new();
2548 loop {
2549 cols.push(self.expect_ident_like()?);
2550 match self.peek() {
2551 Token::Comma => {
2552 self.advance();
2553 }
2554 Token::RParen => {
2555 self.advance();
2556 break;
2557 }
2558 other => {
2559 return Err(self.err(format!(
2560 "expected ',' or ')' in INCLUDE list, got {other:?}"
2561 )));
2562 }
2563 }
2564 }
2565 cols
2566 } else {
2567 Vec::new()
2568 };
2569 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2575 self.advance();
2576 if !matches!(self.peek(), Token::LParen) {
2577 return Err(self.err(format!(
2578 "expected '(' after WITH in CREATE INDEX, got {:?}",
2579 self.peek()
2580 )));
2581 }
2582 self.advance();
2583 loop {
2584 if matches!(self.peek(), Token::RParen) {
2585 self.advance();
2586 break;
2587 }
2588 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
2591 self.advance();
2592 let _ = self.advance(); }
2594 match self.peek() {
2595 Token::Comma => {
2596 self.advance();
2597 }
2598 Token::RParen => {
2599 self.advance();
2600 break;
2601 }
2602 other => {
2603 return Err(self.err(format!(
2604 "expected ',' or ')' in WITH (…) clause, got {other:?}"
2605 )));
2606 }
2607 }
2608 }
2609 }
2610 let partial_predicate = if matches!(self.peek(), Token::Where) {
2612 self.advance();
2613 Some(self.parse_expr(0)?)
2614 } else {
2615 None
2616 };
2617 if is_unique && !matches!(method, IndexMethod::BTree) {
2622 return Err(self.err(alloc::format!(
2623 "UNIQUE is only supported on BTree indexes, got USING {:?}",
2624 method
2625 )));
2626 }
2627 Ok(Statement::CreateIndex(CreateIndexStatement {
2628 name,
2629 table,
2630 column,
2631 method,
2632 if_not_exists,
2633 included_columns,
2634 partial_predicate,
2635 extra_columns: extra_columns.clone(),
2636 expression,
2637 is_unique,
2638 }))
2639 }
2640
2641 fn parse_column_def_with_fk(
2646 &mut self,
2647 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
2648 let col = self.parse_column_def()?;
2649 let inline_references = matches!(
2651 self.peek(),
2652 Token::Ident(s) if s.eq_ignore_ascii_case("references")
2653 );
2654 if !inline_references {
2655 return Ok((col, None));
2656 }
2657 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
2658 let fk = ForeignKeyConstraint {
2659 name: None,
2660 columns: vec![col.name.clone()],
2661 parent_table,
2662 parent_columns,
2663 on_delete,
2664 on_update,
2665 };
2666 Ok((col, Some(fk)))
2667 }
2668
2669 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
2670 let name = self.expect_ident_like()?;
2671 let ty_ident = match self.advance() {
2674 Token::Ident(s) => s,
2675 other => {
2676 return Err(ParseError {
2677 message: format!("expected column type, got {other:?}"),
2678 token_pos: self.pos.saturating_sub(1),
2679 });
2680 }
2681 };
2682 let mut implied_auto_increment = false;
2689 let mut implied_not_null = false;
2690 let mut ty = match ty_ident.as_str() {
2691 "smallserial" | "serial2" => {
2693 implied_auto_increment = true;
2694 implied_not_null = true;
2695 ColumnTypeName::SmallInt
2696 }
2697 "serial" | "serial4" => {
2698 implied_auto_increment = true;
2699 implied_not_null = true;
2700 ColumnTypeName::Int
2701 }
2702 "bigserial" | "serial8" => {
2703 implied_auto_increment = true;
2704 implied_not_null = true;
2705 ColumnTypeName::BigInt
2706 }
2707 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
2713 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
2715 "bigint" => ColumnTypeName::BigInt,
2716 "float" | "double" | "real" => ColumnTypeName::Float,
2718 "text" => ColumnTypeName::Text,
2719 "bool" | "boolean" => ColumnTypeName::Bool,
2720 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
2721 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
2722 "vector" => {
2723 let dim = self.parse_paren_size("VECTOR")?;
2724 let encoding = self.parse_optional_vector_encoding()?;
2725 ColumnTypeName::Vector { dim, encoding }
2726 }
2727 "numeric" => {
2728 let (precision, scale) = self.parse_optional_numeric_params()?;
2729 ColumnTypeName::Numeric(precision, scale)
2730 }
2731 "date" => ColumnTypeName::Date,
2732 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
2735 "timestamptz" => ColumnTypeName::Timestamptz,
2739 "json" => ColumnTypeName::Json,
2744 "jsonb" => ColumnTypeName::Jsonb,
2745 "bytea" | "bytes" => ColumnTypeName::Bytes,
2751 "tsvector" => ColumnTypeName::TsVector,
2756 "tsquery" => ColumnTypeName::TsQuery,
2757 other => {
2758 return Err(ParseError {
2759 message: format!("unsupported column type {other:?}"),
2760 token_pos: self.pos.saturating_sub(1),
2761 });
2762 }
2763 };
2764 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
2769 self.advance();
2770 }
2771 if matches!(self.peek(), Token::LBracket) {
2776 self.advance();
2777 if !matches!(self.peek(), Token::RBracket) {
2778 return Err(self.err(alloc::format!(
2779 "TEXT[] takes no dimension; got {:?}",
2780 self.peek()
2781 )));
2782 }
2783 self.advance();
2784 ty = match ty {
2788 ColumnTypeName::Text => ColumnTypeName::TextArray,
2789 ColumnTypeName::Int => ColumnTypeName::IntArray,
2790 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
2791 other => {
2792 return Err(self.err(alloc::format!(
2793 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
2794 )));
2795 }
2796 };
2797 }
2798 let mut default: Option<Expr> = None;
2802 let mut nullable = !implied_not_null;
2803 let mut nullability_seen = implied_not_null;
2804 let mut auto_increment = implied_auto_increment;
2805 let mut is_primary_key = false;
2806 loop {
2807 if matches!(self.peek(), Token::Default) {
2808 if default.is_some() {
2809 return Err(self.err("DEFAULT specified twice".into()));
2810 }
2811 self.advance();
2812 default = Some(self.parse_expr(0)?);
2813 continue;
2814 }
2815 if matches!(self.peek(), Token::Not) {
2816 if nullability_seen {
2817 return Err(self.err("NOT NULL specified twice".into()));
2818 }
2819 self.advance();
2820 if !matches!(self.peek(), Token::Null) {
2821 return Err(self.err(format!(
2822 "expected NULL after NOT in column def, got {:?}",
2823 self.peek()
2824 )));
2825 }
2826 self.advance();
2827 nullable = false;
2828 nullability_seen = true;
2829 continue;
2830 }
2831 if let Token::Ident(s) = self.peek()
2834 && (s.eq_ignore_ascii_case("auto_increment")
2835 || s.eq_ignore_ascii_case("autoincrement"))
2836 {
2837 if auto_increment {
2838 return Err(self.err("AUTO_INCREMENT specified twice".into()));
2839 }
2840 self.advance();
2841 auto_increment = true;
2842 continue;
2843 }
2844 if let Token::Ident(s) = self.peek()
2849 && s.eq_ignore_ascii_case("primary")
2850 {
2851 if is_primary_key {
2852 return Err(self.err("PRIMARY KEY specified twice".into()));
2853 }
2854 let next = self.tokens.get(self.pos + 1);
2856 let next_is_key = matches!(
2857 next,
2858 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
2859 );
2860 if !next_is_key {
2861 return Err(self.err(format!(
2862 "expected KEY after PRIMARY in column def, got {:?}",
2863 next
2864 )));
2865 }
2866 self.advance(); self.advance(); is_primary_key = true;
2869 if nullability_seen && nullable {
2870 return Err(self.err(
2871 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
2872 ));
2873 }
2874 nullable = false;
2875 nullability_seen = true;
2876 continue;
2877 }
2878 break;
2879 }
2880 Ok(ColumnDef {
2881 name,
2882 ty,
2883 nullable,
2884 default,
2885 auto_increment,
2886 is_primary_key,
2887 })
2888 }
2889
2890 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
2894 if !matches!(self.peek(), Token::LParen) {
2895 return Ok((0, 0));
2899 }
2900 self.advance();
2901 let precision = match self.advance() {
2902 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
2903 other => {
2904 return Err(ParseError {
2905 message: format!(
2906 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
2907 ),
2908 token_pos: self.pos.saturating_sub(1),
2909 });
2910 }
2911 };
2912 let scale = if matches!(self.peek(), Token::Comma) {
2913 self.advance();
2914 match self.advance() {
2915 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
2916 u8::try_from(n).expect("range-checked")
2917 }
2918 other => {
2919 return Err(ParseError {
2920 message: format!(
2921 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
2922 ),
2923 token_pos: self.pos.saturating_sub(1),
2924 });
2925 }
2926 }
2927 } else {
2928 0
2929 };
2930 if !matches!(self.peek(), Token::RParen) {
2931 return Err(self.err(format!(
2932 "expected ')' to close NUMERIC params, got {:?}",
2933 self.peek()
2934 )));
2935 }
2936 self.advance();
2937 Ok((precision, scale))
2938 }
2939
2940 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
2948 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2949 return Ok(VecEncoding::F32);
2950 }
2951 self.advance();
2952 let enc_ident = match self.advance() {
2953 Token::Ident(s) => s,
2954 other => {
2955 return Err(self.err(format!(
2956 "expected vector encoding after USING, got {other:?}"
2957 )));
2958 }
2959 };
2960 match enc_ident.to_ascii_lowercase().as_str() {
2961 "sq8" => Ok(VecEncoding::Sq8),
2962 "half" => Ok(VecEncoding::F16),
2965 other => Err(self.err(format!(
2966 "unknown vector encoding {other:?}; supported: SQ8, HALF"
2967 ))),
2968 }
2969 }
2970
2971 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
2972 if !matches!(self.peek(), Token::LParen) {
2973 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
2974 }
2975 self.advance();
2976 let n = match self.advance() {
2977 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
2978 message: format!("{label} size too large: {n}"),
2979 token_pos: self.pos.saturating_sub(1),
2980 })?,
2981 other => {
2982 return Err(ParseError {
2983 message: format!("expected positive integer {label} size, got {other:?}"),
2984 token_pos: self.pos.saturating_sub(1),
2985 });
2986 }
2987 };
2988 if !matches!(self.peek(), Token::RParen) {
2989 return Err(self.err(format!(
2990 "expected ')' after {label} size, got {:?}",
2991 self.peek()
2992 )));
2993 }
2994 self.advance();
2995 Ok(n)
2996 }
2997
2998 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
2999 debug_assert!(matches!(self.peek(), Token::Insert));
3000 self.advance();
3001 if !matches!(self.peek(), Token::Into) {
3002 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
3003 }
3004 self.advance();
3005 let table = self.expect_ident_like()?;
3006 let columns = if matches!(self.peek(), Token::LParen) {
3008 self.advance();
3009 let mut names = Vec::new();
3010 loop {
3011 names.push(self.expect_ident_like()?);
3012 match self.peek() {
3013 Token::Comma => {
3014 self.advance();
3015 }
3016 Token::RParen => {
3017 self.advance();
3018 break;
3019 }
3020 other => {
3021 return Err(self.err(format!(
3022 "expected ',' or ')' in INSERT column list, got {other:?}"
3023 )));
3024 }
3025 }
3026 }
3027 Some(names)
3028 } else {
3029 None
3030 };
3031 if !matches!(self.peek(), Token::Values) {
3032 return Err(self.err(format!(
3033 "expected VALUES after table name, got {:?}",
3034 self.peek()
3035 )));
3036 }
3037 self.advance();
3038 if !matches!(self.peek(), Token::LParen) {
3039 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
3040 }
3041 let mut rows = Vec::new();
3042 loop {
3043 if !matches!(self.peek(), Token::LParen) {
3045 return Err(self.err(format!(
3046 "expected '(' for next VALUES tuple, got {:?}",
3047 self.peek()
3048 )));
3049 }
3050 self.advance();
3051 let mut tuple = Vec::new();
3052 loop {
3053 tuple.push(self.parse_expr(0)?);
3054 match self.peek() {
3055 Token::Comma => {
3056 self.advance();
3057 }
3058 Token::RParen => {
3059 self.advance();
3060 break;
3061 }
3062 other => {
3063 return Err(self.err(format!(
3064 "expected ',' or ')' in VALUES tuple, got {other:?}"
3065 )));
3066 }
3067 }
3068 }
3069 if tuple.is_empty() {
3070 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
3071 }
3072 rows.push(tuple);
3073 if matches!(self.peek(), Token::Comma) {
3075 self.advance();
3076 } else {
3077 break;
3078 }
3079 }
3080 let on_conflict = self.parse_optional_on_conflict()?;
3081 let returning = self.parse_optional_returning()?;
3082 Ok(Statement::Insert(InsertStatement {
3083 table,
3084 columns,
3085 rows,
3086 on_conflict,
3087 returning,
3088 }))
3089 }
3090
3091 fn parse_optional_on_conflict(
3096 &mut self,
3097 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
3098 if !matches!(self.peek(), Token::On) {
3099 return Ok(None);
3100 }
3101 let next_is_conflict = matches!(
3104 self.tokens.get(self.pos + 1),
3105 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
3106 );
3107 if !next_is_conflict {
3108 return Ok(None);
3109 }
3110 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
3114 if matches!(self.peek(), Token::LParen) {
3115 self.advance();
3116 loop {
3117 target_columns.push(self.expect_ident_like()?);
3118 match self.peek() {
3119 Token::Comma => {
3120 self.advance();
3121 }
3122 Token::RParen => {
3123 self.advance();
3124 break;
3125 }
3126 other => {
3127 return Err(self.err(alloc::format!(
3128 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
3129 )));
3130 }
3131 }
3132 }
3133 }
3134 match self.advance() {
3136 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
3137 other => {
3138 return Err(self.err(alloc::format!(
3139 "expected DO after ON CONFLICT [(…)], got {other:?}"
3140 )));
3141 }
3142 }
3143 let action = match self.advance() {
3145 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
3146 crate::ast::OnConflictAction::Nothing
3147 }
3148 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3149 self.parse_on_conflict_update_action()?
3150 }
3151 other => {
3152 return Err(self.err(alloc::format!(
3153 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
3154 )));
3155 }
3156 };
3157 Ok(Some(crate::ast::OnConflictClause {
3158 target_columns,
3159 action,
3160 }))
3161 }
3162
3163 fn parse_on_conflict_update_action(
3167 &mut self,
3168 ) -> Result<crate::ast::OnConflictAction, ParseError> {
3169 match self.advance() {
3171 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
3172 other => {
3173 return Err(self.err(alloc::format!(
3174 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
3175 )));
3176 }
3177 }
3178 let mut assignments: Vec<(String, Expr)> = Vec::new();
3179 loop {
3180 let col = self.expect_ident_like()?;
3181 if !matches!(self.peek(), Token::Eq) {
3182 return Err(self.err(alloc::format!(
3183 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
3184 self.peek()
3185 )));
3186 }
3187 self.advance();
3188 let value = self.parse_expr(0)?;
3189 assignments.push((col, value));
3190 if matches!(self.peek(), Token::Comma) {
3191 self.advance();
3192 continue;
3193 }
3194 break;
3195 }
3196 let where_ = if matches!(self.peek(), Token::Where) {
3197 self.advance();
3198 Some(self.parse_expr(0)?)
3199 } else {
3200 None
3201 };
3202 Ok(crate::ast::OnConflictAction::Update {
3203 assignments,
3204 where_,
3205 })
3206 }
3207
3208 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
3209 let mut items = Vec::new();
3210 loop {
3211 items.push(self.parse_select_item()?);
3212 if matches!(self.peek(), Token::Comma) {
3213 self.advance();
3214 } else {
3215 break;
3216 }
3217 }
3218 Ok(items)
3219 }
3220
3221 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
3222 if matches!(self.peek(), Token::Star) {
3223 self.advance();
3224 return Ok(SelectItem::Wildcard);
3225 }
3226 let expr = self.parse_expr(0)?;
3227 let alias = self.parse_optional_alias();
3228 Ok(SelectItem::Expr { expr, alias })
3229 }
3230
3231 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
3232 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
3236 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
3237 {
3238 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
3241 if !matches!(self.peek(), Token::RParen) {
3242 return Err(self.err(alloc::format!(
3243 "expected ')' after unnest() argument, got {:?}",
3244 self.peek()
3245 )));
3246 }
3247 self.advance();
3248 let alias_ident = self.parse_optional_alias();
3249 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
3250 return Ok(TableRef {
3251 name,
3252 alias: alias_ident,
3253 as_of_segment: None,
3254 unnest_expr: Some(Box::new(expr)),
3255 });
3256 }
3257 let name = self.expect_ident_like()?;
3258 let as_of_segment = if matches!(self.peek(), Token::As)
3264 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
3265 {
3266 self.advance(); self.advance(); let kw = match self.peek().clone() {
3269 Token::Ident(s) | Token::QuotedIdent(s) => s,
3270 other => {
3271 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
3272 }
3273 };
3274 if !kw.eq_ignore_ascii_case("segment") {
3275 return Err(self.err(format!(
3276 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
3277 )));
3278 }
3279 self.advance();
3280 let id = match self.advance() {
3283 Token::String(s) => s
3284 .parse::<u32>()
3285 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3286 Token::Integer(n) => u32::try_from(n)
3287 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3288 other => {
3289 return Err(self.err(format!(
3290 "expected segment id literal after AS OF SEGMENT, got {other:?}"
3291 )));
3292 }
3293 };
3294 Some(id)
3295 } else {
3296 None
3297 };
3298 let alias = self.parse_optional_alias();
3299 Ok(TableRef {
3300 name,
3301 alias,
3302 as_of_segment,
3303 unnest_expr: None,
3304 })
3305 }
3306
3307 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
3312 let primary = self.parse_table_ref()?;
3313 let mut joins = Vec::new();
3314 loop {
3315 if matches!(self.peek(), Token::Comma) {
3317 self.advance();
3318 let table = self.parse_table_ref()?;
3319 joins.push(FromJoin {
3320 kind: JoinKind::Cross,
3321 table,
3322 on: None,
3323 });
3324 continue;
3325 }
3326 let kind =
3329 match self.peek() {
3330 Token::Inner => {
3331 self.advance();
3332 if !matches!(self.peek(), Token::Join) {
3333 return Err(self
3334 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
3335 }
3336 self.advance();
3337 JoinKind::Inner
3338 }
3339 Token::Left => {
3340 self.advance();
3341 if matches!(self.peek(), Token::Outer) {
3342 self.advance();
3343 }
3344 if !matches!(self.peek(), Token::Join) {
3345 return Err(self.err(format!(
3346 "expected JOIN after LEFT [OUTER], got {:?}",
3347 self.peek()
3348 )));
3349 }
3350 self.advance();
3351 JoinKind::Left
3352 }
3353 Token::Cross => {
3354 self.advance();
3355 if !matches!(self.peek(), Token::Join) {
3356 return Err(self
3357 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
3358 }
3359 self.advance();
3360 JoinKind::Cross
3361 }
3362 Token::Join => {
3363 self.advance();
3364 JoinKind::Inner
3365 }
3366 _ => break,
3367 };
3368 let table = self.parse_table_ref()?;
3369 let on = if matches!(self.peek(), Token::On) {
3370 self.advance();
3371 Some(self.parse_expr(0)?)
3372 } else if kind == JoinKind::Cross {
3373 None
3374 } else {
3375 return Err(self.err(format!(
3376 "expected ON after {:?} JOIN, got {:?}",
3377 kind,
3378 self.peek()
3379 )));
3380 };
3381 joins.push(FromJoin { kind, table, on });
3382 }
3383 Ok(FromClause { primary, joins })
3384 }
3385
3386 fn parse_optional_alias(&mut self) -> Option<String> {
3391 if matches!(self.peek(), Token::As) {
3392 self.advance();
3393 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3398 return self.expect_ident_like().ok();
3399 }
3400 return None;
3401 }
3402 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3403 return self.expect_ident_like().ok();
3404 }
3405 None
3406 }
3407
3408 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
3410 let mut lhs = self.parse_unary()?;
3411 while let Some((op, prec)) = binop_from(self.peek()) {
3412 if prec < min_prec {
3413 break;
3414 }
3415 self.advance();
3416 let any_kind = match self.peek() {
3421 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
3422 Some(false)
3423 }
3424 Token::Ident(s) | Token::QuotedIdent(s)
3425 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
3426 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
3427 {
3428 Some(s.eq_ignore_ascii_case("any"))
3429 }
3430 _ => None,
3431 };
3432 if let Some(is_any) = any_kind {
3433 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
3436 if !matches!(self.peek(), Token::RParen) {
3437 return Err(self.err(alloc::format!(
3438 "expected ')' after ANY/ALL argument, got {:?}",
3439 self.peek()
3440 )));
3441 }
3442 self.advance();
3443 lhs = Expr::AnyAll {
3444 expr: Box::new(lhs),
3445 op,
3446 array: Box::new(arr),
3447 is_any,
3448 };
3449 continue;
3450 }
3451 let rhs = self.parse_expr(prec + 1)?;
3452 lhs = Expr::Binary {
3453 lhs: Box::new(lhs),
3454 op,
3455 rhs: Box::new(rhs),
3456 };
3457 }
3458 Ok(lhs)
3459 }
3460
3461 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
3462 match self.peek() {
3463 Token::Not => {
3464 self.advance();
3465 let e = self.parse_expr(3)?;
3468 Ok(Expr::Unary {
3469 op: UnOp::Not,
3470 expr: Box::new(e),
3471 })
3472 }
3473 Token::Minus => {
3474 self.advance();
3475 let e = self.parse_expr(8)?;
3478 Ok(Expr::Unary {
3479 op: UnOp::Neg,
3480 expr: Box::new(e),
3481 })
3482 }
3483 _ => self.parse_atom(),
3484 }
3485 }
3486
3487 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
3488 let tok_pos = self.pos;
3489 match self.advance() {
3490 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
3491 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
3492 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
3493 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
3494 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
3495 Token::Null => Ok(Expr::Literal(Literal::Null)),
3496 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
3500 Token::LParen => {
3501 if matches!(self.peek(), Token::Select) {
3505 let inner = self.parse_select_stmt()?;
3506 match self.advance() {
3507 Token::RParen => {
3508 let Statement::Select(s) = inner else {
3509 unreachable!("parse_select_stmt returns Select")
3510 };
3511 Ok(Expr::ScalarSubquery(Box::new(s)))
3512 }
3513 other => Err(ParseError {
3514 message: format!("expected ')' after scalar subquery, got {other:?}"),
3515 token_pos: self.pos.saturating_sub(1),
3516 }),
3517 }
3518 } else {
3519 let e = self.parse_expr(0)?;
3520 match self.advance() {
3521 Token::RParen => Ok(e),
3522 other => Err(ParseError {
3523 message: format!("expected ')', got {other:?}"),
3524 token_pos: self.pos.saturating_sub(1),
3525 }),
3526 }
3527 }
3528 }
3529 Token::LBracket => self.parse_vector_literal_body(),
3530 Token::Extract => self.parse_extract_atom(),
3531 Token::Interval => self.parse_interval_atom(),
3532 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
3537 self.parse_exists_atom(false)
3538 }
3539 Token::Ident(s) | Token::QuotedIdent(s)
3543 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
3544 {
3545 self.advance(); let mut items: Vec<Expr> = Vec::new();
3547 if !matches!(self.peek(), Token::RBracket) {
3548 loop {
3549 items.push(self.parse_expr(0)?);
3550 match self.peek() {
3551 Token::Comma => {
3552 self.advance();
3553 }
3554 Token::RBracket => break,
3555 other => {
3556 return Err(self.err(alloc::format!(
3557 "expected ',' or ']' in ARRAY literal, got {other:?}"
3558 )));
3559 }
3560 }
3561 }
3562 }
3563 self.advance(); Ok(Expr::Array(items))
3565 }
3566 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
3567 other => Err(ParseError {
3568 message: format!("unexpected token {other:?} in expression"),
3569 token_pos: tok_pos,
3570 }),
3571 }
3572 .and_then(|atom| self.finish_postfix_casts(atom))
3574 }
3575
3576 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
3579 loop {
3580 if matches!(self.peek(), Token::DoubleColon) {
3581 self.advance();
3582 let target = match self.advance() {
3587 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
3588 "int" | "integer" | "int4" => {
3589 if matches!(self.peek(), Token::LBracket)
3590 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3591 {
3592 self.advance();
3593 self.advance();
3594 CastTarget::IntArray
3595 } else {
3596 CastTarget::Int
3597 }
3598 }
3599 "bigint" | "int8" => {
3600 if matches!(self.peek(), Token::LBracket)
3601 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3602 {
3603 self.advance();
3604 self.advance();
3605 CastTarget::BigIntArray
3606 } else {
3607 CastTarget::BigInt
3608 }
3609 }
3610 "float" | "double" | "real" => CastTarget::Float,
3611 "text" => {
3612 if matches!(self.peek(), Token::LBracket)
3614 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3615 {
3616 self.advance();
3617 self.advance();
3618 CastTarget::TextArray
3619 } else {
3620 CastTarget::Text
3621 }
3622 }
3623 "bool" | "boolean" => CastTarget::Bool,
3624 "vector" => CastTarget::Vector,
3625 "date" => CastTarget::Date,
3626 "timestamp" | "datetime" => CastTarget::Timestamp,
3627 "timestamptz" => CastTarget::Timestamptz,
3628 "interval" => CastTarget::Interval,
3629 "json" => CastTarget::Json,
3630 "jsonb" => CastTarget::Jsonb,
3631 "regtype" => CastTarget::RegType,
3632 "regclass" => CastTarget::RegClass,
3633 "tsvector" => CastTarget::TsVector,
3637 "tsquery" => CastTarget::TsQuery,
3638 other => {
3639 return Err(ParseError {
3640 message: format!("unsupported cast target `::{other}`"),
3641 token_pos: self.pos.saturating_sub(1),
3642 });
3643 }
3644 },
3645 Token::Interval => CastTarget::Interval,
3646 other => {
3647 return Err(ParseError {
3648 message: format!("expected type ident after `::`, got {other:?}"),
3649 token_pos: self.pos.saturating_sub(1),
3650 });
3651 }
3652 };
3653 expr = Expr::Cast {
3654 expr: Box::new(expr),
3655 target,
3656 };
3657 continue;
3658 }
3659 if matches!(self.peek(), Token::Is) {
3660 self.advance();
3661 let negated = if matches!(self.peek(), Token::Not) {
3662 self.advance();
3663 true
3664 } else {
3665 false
3666 };
3667 if matches!(self.peek(), Token::Distinct) {
3670 self.advance();
3671 if !matches!(self.peek(), Token::From) {
3672 return Err(self.err(format!(
3673 "expected FROM after IS{} DISTINCT, got {:?}",
3674 if negated { " NOT" } else { "" },
3675 self.peek()
3676 )));
3677 }
3678 self.advance();
3679 let rhs = self.parse_expr(20)?;
3683 let op = if negated {
3684 BinOp::IsNotDistinctFrom
3685 } else {
3686 BinOp::IsDistinctFrom
3687 };
3688 expr = Expr::Binary {
3689 op,
3690 lhs: Box::new(expr),
3691 rhs: Box::new(rhs),
3692 };
3693 continue;
3694 }
3695 if !matches!(self.peek(), Token::Null) {
3696 return Err(self.err(format!(
3697 "expected NULL or DISTINCT after IS{}, got {:?}",
3698 if negated { " NOT" } else { "" },
3699 self.peek()
3700 )));
3701 }
3702 self.advance();
3703 expr = Expr::IsNull {
3704 expr: Box::new(expr),
3705 negated,
3706 };
3707 continue;
3708 }
3709 let negated = if matches!(self.peek(), Token::Not) {
3713 let next = self.tokens.get(self.pos + 1);
3714 matches!(next, Some(Token::Between | Token::In | Token::Like))
3715 } else {
3716 false
3717 };
3718 if negated {
3719 self.advance();
3720 }
3721 if matches!(self.peek(), Token::Between) {
3722 expr = self.parse_between_tail(expr, negated)?;
3723 continue;
3724 }
3725 if matches!(self.peek(), Token::In) {
3726 expr = self.parse_in_tail(expr, negated)?;
3727 continue;
3728 }
3729 if matches!(self.peek(), Token::Like) {
3730 self.advance();
3731 let pattern = self.parse_expr(5)?;
3734 expr = Expr::Like {
3735 expr: Box::new(expr),
3736 pattern: Box::new(pattern),
3737 negated,
3738 };
3739 continue;
3740 }
3741 if matches!(self.peek(), Token::LBracket) {
3745 self.advance();
3746 let index = self.parse_expr(0)?;
3747 if !matches!(self.peek(), Token::RBracket) {
3748 return Err(self.err(alloc::format!(
3749 "expected ']' after array index, got {:?}",
3750 self.peek()
3751 )));
3752 }
3753 self.advance();
3754 expr = Expr::ArraySubscript {
3755 target: Box::new(expr),
3756 index: Box::new(index),
3757 };
3758 continue;
3759 }
3760 return Ok(expr);
3761 }
3762 }
3763
3764 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
3768 self.advance(); let low = self.parse_expr(5)?;
3770 if !matches!(self.peek(), Token::And) {
3771 return Err(self.err(format!(
3772 "expected AND after BETWEEN low bound, got {:?}",
3773 self.peek()
3774 )));
3775 }
3776 self.advance();
3777 let high = self.parse_expr(5)?;
3778 let target = Box::new(expr);
3779 let combined = Expr::Binary {
3780 lhs: Box::new(Expr::Binary {
3781 lhs: target.clone(),
3782 op: BinOp::GtEq,
3783 rhs: Box::new(low),
3784 }),
3785 op: BinOp::And,
3786 rhs: Box::new(Expr::Binary {
3787 lhs: target,
3788 op: BinOp::LtEq,
3789 rhs: Box::new(high),
3790 }),
3791 };
3792 Ok(maybe_not(combined, negated))
3793 }
3794
3795 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
3800 let mut recursive = false;
3805 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3806 && s.eq_ignore_ascii_case("recursive")
3807 {
3808 self.advance();
3809 recursive = true;
3810 }
3811 let mut ctes = Vec::new();
3812 loop {
3813 let name = self.expect_ident_like()?;
3814 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
3818 self.advance();
3819 let mut names = Vec::new();
3820 loop {
3821 names.push(self.expect_ident_like()?);
3822 if matches!(self.peek(), Token::Comma) {
3823 self.advance();
3824 continue;
3825 }
3826 break;
3827 }
3828 if !matches!(self.peek(), Token::RParen) {
3829 return Err(self.err(format!(
3830 "expected ')' to close CTE column list, got {:?}",
3831 self.peek()
3832 )));
3833 }
3834 self.advance();
3835 names
3836 } else {
3837 Vec::new()
3838 };
3839 if !matches!(self.peek(), Token::As) {
3843 return Err(self.err(format!(
3844 "expected AS after CTE name {name:?}, got {:?}",
3845 self.peek()
3846 )));
3847 }
3848 self.advance();
3849 if !matches!(self.peek(), Token::LParen) {
3850 return Err(self.err(format!(
3851 "expected '(' after AS in WITH clause, got {:?}",
3852 self.peek()
3853 )));
3854 }
3855 self.advance();
3856 if !matches!(self.peek(), Token::Select) {
3857 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
3858 }
3859 let inner = self.parse_select_stmt()?;
3860 if !matches!(self.peek(), Token::RParen) {
3861 return Err(self.err(format!(
3862 "expected ')' after CTE body, got {:?}",
3863 self.peek()
3864 )));
3865 }
3866 self.advance();
3867 let Statement::Select(body) = inner else {
3868 unreachable!("parse_select_stmt returns Select")
3869 };
3870 ctes.push(crate::ast::Cte {
3871 name,
3872 body,
3873 recursive,
3874 column_overrides,
3875 });
3876 if matches!(self.peek(), Token::Comma) {
3877 self.advance();
3878 continue;
3879 }
3880 break;
3881 }
3882 if !matches!(self.peek(), Token::Select) {
3884 return Err(self.err(format!(
3885 "expected SELECT after WITH clause, got {:?}",
3886 self.peek()
3887 )));
3888 }
3889 let body_stmt = self.parse_select_stmt()?;
3890 let Statement::Select(mut body) = body_stmt else {
3891 unreachable!()
3892 };
3893 body.ctes = ctes;
3894 Ok(Statement::Select(body))
3895 }
3896
3897 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
3901 if !matches!(self.peek(), Token::LParen) {
3902 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
3903 }
3904 self.advance();
3905 let inner = self.parse_select_stmt()?;
3906 if !matches!(self.peek(), Token::RParen) {
3907 return Err(self.err(format!(
3908 "expected ')' after EXISTS-subquery, got {:?}",
3909 self.peek()
3910 )));
3911 }
3912 self.advance();
3913 let Statement::Select(s) = inner else {
3914 unreachable!("parse_select_stmt returns Select")
3915 };
3916 Ok(Expr::Exists {
3917 subquery: Box::new(s),
3918 negated,
3919 })
3920 }
3921
3922 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
3923 self.advance(); if !matches!(self.peek(), Token::LParen) {
3925 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
3926 }
3927 self.advance();
3928 if matches!(self.peek(), Token::Select) {
3930 let inner = self.parse_select_stmt()?;
3931 if !matches!(self.peek(), Token::RParen) {
3932 return Err(self.err(format!(
3933 "expected ')' after IN-subquery, got {:?}",
3934 self.peek()
3935 )));
3936 }
3937 self.advance();
3938 let Statement::Select(s) = inner else {
3939 unreachable!("parse_select_stmt always returns Statement::Select")
3940 };
3941 return Ok(Expr::InSubquery {
3942 expr: Box::new(expr),
3943 subquery: Box::new(s),
3944 negated,
3945 });
3946 }
3947 let mut elements = Vec::new();
3948 if !matches!(self.peek(), Token::RParen) {
3949 loop {
3950 elements.push(self.parse_expr(0)?);
3951 match self.peek() {
3952 Token::Comma => {
3953 self.advance();
3954 }
3955 Token::RParen => break,
3956 other => {
3957 return Err(
3958 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
3959 );
3960 }
3961 }
3962 }
3963 }
3964 self.advance(); let target = Box::new(expr);
3966 let combined = if elements.is_empty() {
3967 Expr::Literal(Literal::Bool(false))
3968 } else {
3969 let mut iter = elements.into_iter();
3970 let first = iter.next().unwrap();
3971 let mut acc = Expr::Binary {
3972 lhs: target.clone(),
3973 op: BinOp::Eq,
3974 rhs: Box::new(first),
3975 };
3976 for elt in iter {
3977 acc = Expr::Binary {
3978 lhs: Box::new(acc),
3979 op: BinOp::Or,
3980 rhs: Box::new(Expr::Binary {
3981 lhs: target.clone(),
3982 op: BinOp::Eq,
3983 rhs: Box::new(elt),
3984 }),
3985 };
3986 }
3987 acc
3988 };
3989 Ok(maybe_not(combined, negated))
3990 }
3991
3992 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
4000 if !matches!(self.peek(), Token::LParen) {
4001 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
4002 }
4003 self.advance();
4004 let field_name = self.expect_ident_like()?;
4005 let field = match field_name.to_ascii_lowercase().as_str() {
4006 "year" => ExtractField::Year,
4007 "month" => ExtractField::Month,
4008 "day" => ExtractField::Day,
4009 "hour" => ExtractField::Hour,
4010 "minute" => ExtractField::Minute,
4011 "second" => ExtractField::Second,
4012 "microsecond" | "microseconds" => ExtractField::Microsecond,
4013 other => {
4014 return Err(self.err(format!(
4015 "unknown EXTRACT field {other:?}; \
4016 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
4017 )));
4018 }
4019 };
4020 if !matches!(self.peek(), Token::From) {
4021 return Err(self.err(format!(
4022 "expected FROM after EXTRACT field, got {:?}",
4023 self.peek()
4024 )));
4025 }
4026 self.advance();
4027 let source = self.parse_expr(0)?;
4028 if !matches!(self.peek(), Token::RParen) {
4029 return Err(self.err(format!(
4030 "expected ')' to close EXTRACT, got {:?}",
4031 self.peek()
4032 )));
4033 }
4034 self.advance();
4035 Ok(Expr::Extract {
4036 field,
4037 source: Box::new(source),
4038 })
4039 }
4040
4041 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
4046 let tok = self.advance();
4047 let Token::String(text) = tok else {
4048 return Err(self.err(format!(
4049 "expected string literal after INTERVAL, got {tok:?}"
4050 )));
4051 };
4052 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
4053 message: format!(
4054 "cannot parse INTERVAL {text:?}; \
4055 expected `<n> <unit> [<n> <unit> ...]` with units \
4056 microsecond[s], millisecond[s], second[s], minute[s], \
4057 hour[s], day[s], week[s], month[s], year[s]"
4058 ),
4059 token_pos: self.pos.saturating_sub(1),
4060 })?;
4061 Ok(Expr::Literal(Literal::Interval {
4062 months,
4063 micros,
4064 text,
4065 }))
4066 }
4067
4068 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
4069 let mut elems = Vec::new();
4070 if matches!(self.peek(), Token::RBracket) {
4071 self.advance();
4072 return Ok(Expr::Literal(Literal::Vector(elems)));
4073 }
4074 loop {
4075 let e = self.parse_expr(0)?;
4076 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
4077 message: format!("vector element must be a numeric literal, got {e:?}"),
4078 token_pos: self.pos,
4079 })?;
4080 elems.push(x);
4081 match self.peek() {
4082 Token::Comma => {
4083 self.advance();
4084 }
4085 Token::RBracket => {
4086 self.advance();
4087 break;
4088 }
4089 other => {
4090 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
4091 }
4092 }
4093 }
4094 Ok(Expr::Literal(Literal::Vector(elems)))
4095 }
4096
4097 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
4106 let Token::Ident(s) = self.peek().clone() else {
4107 return NullTreatment::Respect;
4108 };
4109 let is_ignore = s.eq_ignore_ascii_case("ignore");
4110 let is_respect = s.eq_ignore_ascii_case("respect");
4111 if !is_ignore && !is_respect {
4112 return NullTreatment::Respect;
4113 }
4114 if self.pos + 1 < self.tokens.len()
4117 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
4118 && s2.eq_ignore_ascii_case("nulls")
4119 {
4120 self.advance();
4121 self.advance();
4122 return if is_ignore {
4123 NullTreatment::Ignore
4124 } else {
4125 NullTreatment::Respect
4126 };
4127 }
4128 NullTreatment::Respect
4129 }
4130
4131 #[allow(clippy::type_complexity)] fn parse_over_clause(
4134 &mut self,
4135 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
4136 if !matches!(self.peek(), Token::LParen) {
4137 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
4138 }
4139 self.advance();
4140 let mut partition_by = Vec::new();
4141 let mut order_by = Vec::new();
4142 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4144 && s.eq_ignore_ascii_case("partition")
4145 {
4146 self.advance();
4147 if !matches!(self.peek(), Token::By) {
4148 return Err(self.err(format!(
4149 "expected BY after PARTITION, got {:?}",
4150 self.peek()
4151 )));
4152 }
4153 self.advance();
4154 loop {
4155 partition_by.push(self.parse_expr(0)?);
4156 if matches!(self.peek(), Token::Comma) {
4157 self.advance();
4158 continue;
4159 }
4160 break;
4161 }
4162 }
4163 if matches!(self.peek(), Token::Order) {
4165 self.advance();
4166 if !matches!(self.peek(), Token::By) {
4167 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4168 }
4169 self.advance();
4170 loop {
4171 let e = self.parse_expr(0)?;
4172 let desc = if matches!(self.peek(), Token::Desc) {
4173 self.advance();
4174 true
4175 } else if matches!(self.peek(), Token::Asc) {
4176 self.advance();
4177 false
4178 } else {
4179 false
4180 };
4181 order_by.push((e, desc));
4182 if matches!(self.peek(), Token::Comma) {
4183 self.advance();
4184 continue;
4185 }
4186 break;
4187 }
4188 }
4189 let mut frame: Option<WindowFrame> = None;
4193 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
4194 let kind = if s.eq_ignore_ascii_case("rows") {
4195 Some(FrameKind::Rows)
4196 } else if s.eq_ignore_ascii_case("range") {
4197 Some(FrameKind::Range)
4198 } else {
4199 None
4200 };
4201 if let Some(kind) = kind {
4202 self.advance();
4203 frame = Some(self.parse_frame_tail(kind)?);
4204 }
4205 }
4206 if !matches!(self.peek(), Token::RParen) {
4207 return Err(self.err(format!(
4208 "expected ')' to close OVER clause, got {:?}",
4209 self.peek()
4210 )));
4211 }
4212 self.advance();
4213 Ok((partition_by, order_by, frame))
4214 }
4215
4216 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
4222 if matches!(self.peek(), Token::Between) {
4223 self.advance();
4224 let start = self.parse_frame_bound()?;
4225 if !matches!(self.peek(), Token::And) {
4226 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
4227 }
4228 self.advance();
4229 let end = self.parse_frame_bound()?;
4230 Ok(WindowFrame {
4231 kind,
4232 start,
4233 end: Some(end),
4234 })
4235 } else {
4236 let start = self.parse_frame_bound()?;
4237 Ok(WindowFrame {
4238 kind,
4239 start,
4240 end: None,
4241 })
4242 }
4243 }
4244
4245 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
4248 if let Token::Integer(n) = *self.peek() {
4250 self.advance();
4251 let n: u64 = u64::try_from(n).map_err(|_| {
4252 self.err(format!(
4253 "invalid frame offset {n} — expected non-negative integer"
4254 ))
4255 })?;
4256 let dir = self.expect_ident_like()?;
4257 return if dir.eq_ignore_ascii_case("preceding") {
4258 Ok(FrameBound::OffsetPreceding(n))
4259 } else if dir.eq_ignore_ascii_case("following") {
4260 Ok(FrameBound::OffsetFollowing(n))
4261 } else {
4262 Err(self.err(format!(
4263 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
4264 )))
4265 };
4266 }
4267 let first = self.expect_ident_like()?;
4268 if first.eq_ignore_ascii_case("unbounded") {
4269 let dir = self.expect_ident_like()?;
4270 return if dir.eq_ignore_ascii_case("preceding") {
4271 Ok(FrameBound::UnboundedPreceding)
4272 } else if dir.eq_ignore_ascii_case("following") {
4273 Ok(FrameBound::UnboundedFollowing)
4274 } else {
4275 Err(self.err(format!(
4276 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
4277 )))
4278 };
4279 }
4280 if first.eq_ignore_ascii_case("current") {
4281 let row = self.expect_ident_like()?;
4282 if !row.eq_ignore_ascii_case("row") {
4283 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
4284 }
4285 return Ok(FrameBound::CurrentRow);
4286 }
4287 Err(self.err(format!(
4288 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
4289 )))
4290 }
4291
4292 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
4293 if matches!(self.peek(), Token::Dot) {
4294 self.advance();
4295 let name = self.expect_ident_like()?;
4296 return Ok(Expr::Column(ColumnName {
4297 qualifier: Some(first),
4298 name,
4299 }));
4300 }
4301 if matches!(self.peek(), Token::LParen) {
4302 self.advance();
4303 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
4307 self.advance();
4308 if !matches!(self.peek(), Token::RParen) {
4309 return Err(self.err(format!(
4310 "expected ')' after COUNT(*), got {:?}",
4311 self.peek()
4312 )));
4313 }
4314 self.advance();
4315 let null_treatment = self.parse_null_treatment_modifier();
4317 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4318 && s.eq_ignore_ascii_case("over")
4319 {
4320 self.advance();
4321 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4322 return Ok(Expr::WindowFunction {
4323 name: "count_star".into(),
4324 args: Vec::new(),
4325 partition_by,
4326 order_by,
4327 frame,
4328 null_treatment,
4329 });
4330 }
4331 return Ok(Expr::FunctionCall {
4332 name: "count_star".into(),
4333 args: Vec::new(),
4334 });
4335 }
4336 let mut args = Vec::new();
4338 if !matches!(self.peek(), Token::RParen) {
4339 loop {
4340 args.push(self.parse_expr(0)?);
4341 match self.peek() {
4342 Token::Comma => {
4343 self.advance();
4344 }
4345 Token::RParen => break,
4346 other => {
4347 return Err(self.err(format!(
4348 "expected ',' or ')' in function args, got {other:?}"
4349 )));
4350 }
4351 }
4352 }
4353 }
4354 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
4362 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4363 && s.eq_ignore_ascii_case("over")
4364 {
4365 self.advance();
4366 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4367 return Ok(Expr::WindowFunction {
4368 name: first,
4369 args,
4370 partition_by,
4371 order_by,
4372 frame,
4373 null_treatment,
4374 });
4375 }
4376 return Ok(Expr::FunctionCall { name: first, args });
4377 }
4378 let lc = first.to_ascii_lowercase();
4384 if matches!(
4385 lc.as_str(),
4386 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
4387 ) {
4388 return Ok(Expr::FunctionCall {
4389 name: lc,
4390 args: Vec::new(),
4391 });
4392 }
4393 Ok(Expr::Column(ColumnName {
4394 qualifier: None,
4395 name: first,
4396 }))
4397 }
4398}
4399
4400fn extract_first_column(expr: &Expr) -> Option<String> {
4408 match expr {
4409 Expr::Column(cn) => Some(cn.name.clone()),
4410 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
4411 Expr::Binary { lhs, rhs, .. } => {
4412 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
4413 }
4414 Expr::Unary { expr: e, .. } => extract_first_column(e),
4415 _ => None,
4416 }
4417}
4418
4419fn maybe_not(expr: Expr, negated: bool) -> Expr {
4420 if negated {
4421 Expr::Unary {
4422 op: UnOp::Not,
4423 expr: Box::new(expr),
4424 }
4425 } else {
4426 expr
4427 }
4428}
4429
4430fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
4431 let pair = match tok {
4432 Token::Or => (BinOp::Or, 1),
4433 Token::And => (BinOp::And, 2),
4434 Token::Eq => (BinOp::Eq, 4),
4435 Token::NotEq => (BinOp::NotEq, 4),
4436 Token::Lt => (BinOp::Lt, 4),
4437 Token::LtEq => (BinOp::LtEq, 4),
4438 Token::Gt => (BinOp::Gt, 4),
4439 Token::GtEq => (BinOp::GtEq, 4),
4440 Token::L2Distance => (BinOp::L2Distance, 5),
4443 Token::InnerProduct => (BinOp::InnerProduct, 5),
4444 Token::CosineDistance => (BinOp::CosineDistance, 5),
4445 Token::Plus => (BinOp::Add, 6),
4446 Token::Minus => (BinOp::Sub, 6),
4447 Token::Concat => (BinOp::Concat, 6),
4450 Token::Star => (BinOp::Mul, 7),
4451 Token::Slash => (BinOp::Div, 7),
4452 Token::JsonGet => (BinOp::JsonGet, 7),
4456 Token::JsonGetText => (BinOp::JsonGetText, 7),
4457 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
4458 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
4459 Token::JsonContains => (BinOp::JsonContains, 7),
4460 Token::TsMatch => (BinOp::TsMatch, 4),
4464 _ => return None,
4465 };
4466 Some(pair)
4467}
4468
4469#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
4470fn extract_numeric_literal(e: &Expr) -> Option<f32> {
4475 match e {
4476 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
4477 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
4478 Expr::Unary {
4479 op: UnOp::Neg,
4480 expr,
4481 } => extract_numeric_literal(expr).map(|x| -x),
4482 _ => None,
4483 }
4484}
4485
4486pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
4494 let parts: Vec<&str> = s.split_whitespace().collect();
4495 if parts.is_empty() || !parts.len().is_multiple_of(2) {
4496 return None;
4497 }
4498 let mut months: i32 = 0;
4499 let mut micros: i64 = 0;
4500 let mut i = 0;
4501 while i < parts.len() {
4502 let n: i64 = parts[i].parse().ok()?;
4503 let unit = parts[i + 1].to_ascii_lowercase();
4504 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
4505 match unit_stripped {
4506 "microsecond" => micros = micros.checked_add(n)?,
4507 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
4508 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
4509 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
4510 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
4511 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
4512 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
4513 "month" => {
4514 let n32 = i32::try_from(n).ok()?;
4515 months = months.checked_add(n32)?;
4516 }
4517 "year" => {
4518 let n32 = i32::try_from(n).ok()?;
4519 months = months.checked_add(n32.checked_mul(12)?)?;
4520 }
4521 _ => return None,
4522 }
4523 i += 2;
4524 }
4525 Some((months, micros))
4526}
4527
4528fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
4539 Some(match ident.to_ascii_lowercase().as_str() {
4540 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
4541 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
4542 "bigint" => ColumnTypeName::BigInt,
4543 "float" | "double" | "real" => ColumnTypeName::Float,
4544 "text" => ColumnTypeName::Text,
4545 "bool" | "boolean" => ColumnTypeName::Bool,
4546 "date" => ColumnTypeName::Date,
4547 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
4548 "timestamptz" => ColumnTypeName::Timestamptz,
4549 "json" => ColumnTypeName::Json,
4550 "jsonb" => ColumnTypeName::Jsonb,
4551 "bytea" | "bytes" => ColumnTypeName::Bytes,
4552 "tsvector" => ColumnTypeName::TsVector,
4553 "tsquery" => ColumnTypeName::TsQuery,
4554 _ => return None,
4555 })
4556}
4557
4558pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
4585 parse_plpgsql_body(body)
4586}
4587
4588fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
4589 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
4593 message: alloc::format!("plpgsql body lex error: {e}"),
4594 token_pos: 0,
4595 })?;
4596 let mut parser = Parser::new(tokens);
4597 parser.parse_plpgsql_block()
4598}
4599
4600#[cfg(test)]
4601mod tests {
4602 use super::*;
4603 use alloc::string::ToString;
4604
4605 fn parse(s: &str) -> Statement {
4606 parse_statement(s).expect("parse ok")
4607 }
4608
4609 fn lit_int(n: i64) -> Expr {
4610 Expr::Literal(Literal::Integer(n))
4611 }
4612
4613 fn col(name: &str) -> Expr {
4614 Expr::Column(ColumnName {
4615 qualifier: None,
4616 name: name.into(),
4617 })
4618 }
4619
4620 #[test]
4621 fn select_single_integer() {
4622 let s = parse("SELECT 1");
4623 let Statement::Select(s) = s else {
4624 panic!("expected SELECT")
4625 };
4626 assert_eq!(s.items.len(), 1);
4627 assert!(s.from.is_none());
4628 assert!(s.where_.is_none());
4629 }
4630
4631 #[test]
4632 fn select_multiple_literal_kinds() {
4633 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
4634 let Statement::Select(s) = s else {
4635 panic!("expected SELECT")
4636 };
4637 assert_eq!(s.items.len(), 5);
4638 }
4639
4640 #[test]
4641 fn select_wildcard_from_table() {
4642 let s = parse("SELECT * FROM users");
4643 let Statement::Select(s) = s else {
4644 panic!("expected SELECT")
4645 };
4646 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
4647 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
4648 }
4649
4650 #[test]
4651 fn select_with_table_alias() {
4652 let s = parse("SELECT * FROM users AS u");
4653 let Statement::Select(s) = s else {
4654 panic!("expected SELECT")
4655 };
4656 let t = &s.from.as_ref().unwrap().primary;
4657 assert_eq!(t.name, "users");
4658 assert_eq!(t.alias.as_deref(), Some("u"));
4659 }
4660
4661 #[test]
4662 fn select_with_where_eq() {
4663 let s = parse("SELECT a FROM t WHERE a = 1");
4664 let Statement::Select(s) = s else {
4665 panic!("expected SELECT")
4666 };
4667 let w = s.where_.unwrap();
4668 assert_eq!(
4669 w,
4670 Expr::Binary {
4671 lhs: Box::new(col("a")),
4672 op: BinOp::Eq,
4673 rhs: Box::new(lit_int(1)),
4674 }
4675 );
4676 }
4677
4678 #[test]
4679 fn arithmetic_precedence() {
4680 let s = parse("SELECT 1 + 2 * 3");
4681 let Statement::Select(s) = s else {
4682 panic!("expected SELECT")
4683 };
4684 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4685 panic!("wildcard?")
4686 };
4687 assert_eq!(
4688 expr,
4689 &Expr::Binary {
4690 lhs: Box::new(lit_int(1)),
4691 op: BinOp::Add,
4692 rhs: Box::new(Expr::Binary {
4693 lhs: Box::new(lit_int(2)),
4694 op: BinOp::Mul,
4695 rhs: Box::new(lit_int(3)),
4696 }),
4697 }
4698 );
4699 }
4700
4701 #[test]
4702 fn parentheses_override_precedence() {
4703 let s = parse("SELECT (1 + 2) * 3");
4704 let Statement::Select(s) = s else {
4705 panic!("expected SELECT")
4706 };
4707 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4708 panic!()
4709 };
4710 assert_eq!(
4711 expr,
4712 &Expr::Binary {
4713 lhs: Box::new(Expr::Binary {
4714 lhs: Box::new(lit_int(1)),
4715 op: BinOp::Add,
4716 rhs: Box::new(lit_int(2)),
4717 }),
4718 op: BinOp::Mul,
4719 rhs: Box::new(lit_int(3)),
4720 }
4721 );
4722 }
4723
4724 #[test]
4725 fn not_binds_below_comparison() {
4726 let s = parse("SELECT NOT a = 1 FROM t");
4728 let Statement::Select(s) = s else {
4729 panic!("expected SELECT")
4730 };
4731 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4732 panic!()
4733 };
4734 assert_eq!(
4735 expr,
4736 &Expr::Unary {
4737 op: UnOp::Not,
4738 expr: Box::new(Expr::Binary {
4739 lhs: Box::new(col("a")),
4740 op: BinOp::Eq,
4741 rhs: Box::new(lit_int(1)),
4742 }),
4743 }
4744 );
4745 }
4746
4747 #[test]
4748 fn unary_minus_binds_above_multiplication() {
4749 let s = parse("SELECT -a * 2 FROM t");
4751 let Statement::Select(s) = s else {
4752 panic!("expected SELECT")
4753 };
4754 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4755 panic!()
4756 };
4757 assert_eq!(
4758 expr,
4759 &Expr::Binary {
4760 lhs: Box::new(Expr::Unary {
4761 op: UnOp::Neg,
4762 expr: Box::new(col("a")),
4763 }),
4764 op: BinOp::Mul,
4765 rhs: Box::new(lit_int(2)),
4766 }
4767 );
4768 }
4769
4770 #[test]
4771 fn qualified_column() {
4772 let s = parse("SELECT t.col FROM t");
4773 let Statement::Select(s) = s else {
4774 panic!("expected SELECT")
4775 };
4776 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4777 panic!()
4778 };
4779 assert_eq!(
4780 expr,
4781 &Expr::Column(ColumnName {
4782 qualifier: Some("t".into()),
4783 name: "col".into()
4784 })
4785 );
4786 }
4787
4788 #[test]
4789 fn select_item_alias_with_as() {
4790 let s = parse("SELECT a AS y FROM t");
4791 let Statement::Select(s) = s else {
4792 panic!("expected SELECT")
4793 };
4794 let SelectItem::Expr { alias, .. } = &s.items[0] else {
4795 panic!()
4796 };
4797 assert_eq!(alias.as_deref(), Some("y"));
4798 }
4799
4800 #[test]
4801 fn trailing_semicolon_accepted() {
4802 let s = parse("SELECT 1;");
4803 let Statement::Select(s) = s else {
4804 panic!("expected SELECT")
4805 };
4806 assert_eq!(s.items.len(), 1);
4807 }
4808
4809 #[test]
4810 fn boolean_chain_with_and_or_not() {
4811 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
4813 let Statement::Select(s) = s else {
4814 panic!("expected SELECT")
4815 };
4816 let SelectItem::Expr { expr, .. } = &s.items[0] else {
4817 panic!()
4818 };
4819 let expected = Expr::Binary {
4820 lhs: Box::new(Expr::Unary {
4821 op: UnOp::Not,
4822 expr: Box::new(col("a")),
4823 }),
4824 op: BinOp::Or,
4825 rhs: Box::new(Expr::Binary {
4826 lhs: Box::new(col("b")),
4827 op: BinOp::And,
4828 rhs: Box::new(Expr::Unary {
4829 op: UnOp::Not,
4830 expr: Box::new(col("c")),
4831 }),
4832 }),
4833 };
4834 assert_eq!(expr, &expected);
4835 }
4836
4837 #[test]
4838 fn empty_input_errors() {
4839 let err = parse_statement("").unwrap_err();
4840 assert!(err.message.contains("SELECT"));
4841 }
4842
4843 #[test]
4844 fn unmatched_paren_errors() {
4845 assert!(parse_statement("SELECT (1 + 2").is_err());
4846 }
4847
4848 #[test]
4849 fn display_round_trip_simple_select() {
4850 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
4851 let text = original.to_string();
4852 let again = parse_statement(&text).expect("re-parse");
4853 assert_eq!(original, again);
4854 }
4855
4856 #[test]
4859 fn create_table_single_column() {
4860 let s = parse("CREATE TABLE foo (a INT)");
4861 let Statement::CreateTable(c) = s else {
4862 panic!("expected CreateTable")
4863 };
4864 assert_eq!(c.name, "foo");
4865 assert_eq!(c.columns.len(), 1);
4866 assert_eq!(c.columns[0].name, "a");
4867 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
4868 assert!(c.columns[0].nullable);
4869 }
4870
4871 #[test]
4872 fn create_table_multi_column_with_not_null_mix() {
4873 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
4874 let Statement::CreateTable(c) = s else {
4875 panic!()
4876 };
4877 assert_eq!(c.columns.len(), 4);
4878 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
4879 assert!(!c.columns[0].nullable);
4880 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
4881 assert!(c.columns[1].nullable);
4882 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
4883 assert!(!c.columns[2].nullable);
4884 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
4885 }
4886
4887 #[test]
4888 fn create_table_bigint_supported() {
4889 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
4890 let Statement::CreateTable(c) = s else {
4891 panic!()
4892 };
4893 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
4894 }
4895
4896 #[test]
4897 fn create_table_vector_default_is_f32() {
4898 let s = parse("CREATE TABLE t (v VECTOR(128))");
4899 let Statement::CreateTable(c) = s else {
4900 panic!()
4901 };
4902 assert_eq!(
4903 c.columns[0].ty,
4904 ColumnTypeName::Vector {
4905 dim: 128,
4906 encoding: VecEncoding::F32,
4907 },
4908 );
4909 }
4910
4911 #[test]
4912 fn create_table_vector_using_sq8() {
4913 for sql in [
4916 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
4917 "CREATE TABLE t (v VECTOR(128) using sq8)",
4918 ] {
4919 let s = parse(sql);
4920 let Statement::CreateTable(c) = s else {
4921 panic!()
4922 };
4923 assert_eq!(
4924 c.columns[0].ty,
4925 ColumnTypeName::Vector {
4926 dim: 128,
4927 encoding: VecEncoding::Sq8,
4928 },
4929 "{sql}",
4930 );
4931 }
4932 }
4933
4934 #[test]
4935 fn create_table_vector_using_unknown_errors() {
4936 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
4937 assert!(
4938 err.message.contains("unknown vector encoding"),
4939 "got: {}",
4940 err.message
4941 );
4942 }
4943
4944 #[test]
4945 fn vector_using_sq8_display_roundtrips() {
4946 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
4949 let Statement::CreateTable(c) = s else {
4950 panic!()
4951 };
4952 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
4953 }
4954
4955 #[test]
4956 fn parser_recognises_placeholders() {
4957 use crate::ast::{Expr, SelectItem, Statement};
4958 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
4960 let Statement::Select(sel) = s else { panic!() };
4961 assert!(matches!(
4962 sel.items[0],
4963 SelectItem::Expr {
4964 expr: Expr::Placeholder(1),
4965 alias: None
4966 }
4967 ));
4968 let SelectItem::Expr {
4970 expr: Expr::Binary { lhs, rhs, .. },
4971 ..
4972 } = &sel.items[1]
4973 else {
4974 panic!()
4975 };
4976 assert!(matches!(**lhs, Expr::Placeholder(2)));
4977 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
4978 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
4980 panic!()
4981 };
4982 assert!(matches!(**rhs, Expr::Placeholder(3)));
4983 }
4984
4985 #[test]
4986 fn parser_rejects_dollar_zero() {
4987 assert!(parse_statement("SELECT $0").is_err());
4989 }
4990
4991 #[test]
4992 fn placeholder_display_roundtrips() {
4993 let s = parse("SELECT $42 FROM t");
4996 let printed = s.to_string();
4997 assert!(printed.contains("$42"));
4998 let again = parse(&printed);
4999 assert_eq!(s, again);
5000 }
5001
5002 #[test]
5003 fn alter_index_rebuild_bare() {
5004 use crate::ast::{AlterIndexTarget, Statement};
5005 let s = parse("ALTER INDEX my_idx REBUILD");
5006 let Statement::AlterIndex(a) = s else {
5007 panic!("expected AlterIndex, got {s:?}")
5008 };
5009 assert_eq!(a.name, "my_idx");
5010 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
5011 }
5012
5013 #[test]
5014 fn alter_index_rebuild_with_encoding() {
5015 use crate::ast::{AlterIndexTarget, Statement};
5016 for (sql, want) in [
5017 (
5018 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
5019 VecEncoding::F32,
5020 ),
5021 (
5022 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
5023 VecEncoding::Sq8,
5024 ),
5025 (
5026 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5027 VecEncoding::F16,
5028 ),
5029 ] {
5030 let s = parse(sql);
5031 let Statement::AlterIndex(a) = s else {
5032 panic!("{sql}: expected AlterIndex")
5033 };
5034 assert_eq!(a.name, "my_idx");
5035 assert_eq!(
5036 a.target,
5037 AlterIndexTarget::Rebuild {
5038 encoding: Some(want)
5039 },
5040 "{sql}"
5041 );
5042 }
5043 }
5044
5045 #[test]
5046 fn alter_index_rebuild_unknown_encoding_errors() {
5047 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
5048 assert!(
5049 err.message.contains("unknown vector encoding"),
5050 "got: {}",
5051 err.message
5052 );
5053 }
5054
5055 #[test]
5056 fn alter_index_rebuild_display_roundtrips() {
5057 for (input, want) in [
5058 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
5059 (
5060 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5061 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5062 ),
5063 (
5064 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5065 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5066 ),
5067 ] {
5068 let s = parse(input);
5069 assert_eq!(s.to_string(), want);
5070 }
5071 }
5072
5073 #[test]
5074 fn create_table_unknown_type_errors() {
5075 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
5078 assert!(err.message.contains("unsupported column type"));
5079 }
5080
5081 #[test]
5082 fn create_table_missing_table_keyword_errors() {
5083 assert!(parse_statement("CREATE x (a INT)").is_err());
5084 }
5085
5086 #[test]
5087 fn insert_single_value() {
5088 let s = parse("INSERT INTO foo VALUES (42)");
5089 let Statement::Insert(i) = s else {
5090 panic!("expected Insert")
5091 };
5092 assert_eq!(i.table, "foo");
5093 assert_eq!(i.rows.len(), 1);
5094 assert_eq!(i.rows[0].len(), 1);
5095 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
5096 }
5097
5098 #[test]
5099 fn insert_multi_value_with_mixed_literals() {
5100 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
5101 let Statement::Insert(i) = s else { panic!() };
5102 assert_eq!(i.rows.len(), 1);
5103 assert_eq!(i.rows[0].len(), 5);
5104 }
5105
5106 #[test]
5107 fn insert_missing_into_errors() {
5108 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
5109 }
5110
5111 #[test]
5112 fn create_table_round_trip() {
5113 let original =
5114 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
5115 let text = original.to_string();
5116 let again = parse_statement(&text).expect("re-parse");
5117 assert_eq!(original, again);
5118 }
5119
5120 #[test]
5121 fn insert_round_trip_with_negation_and_string() {
5122 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
5123 let text = original.to_string();
5124 let again = parse_statement(&text).expect("re-parse");
5125 assert_eq!(original, again);
5126 }
5127
5128 #[test]
5129 fn unknown_keyword_at_statement_start_errors() {
5130 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
5133 assert!(err.message.contains("expected SELECT"));
5134 }
5135
5136 #[test]
5139 fn create_index_basic() {
5140 let s = parse("CREATE INDEX idx_id ON users (id)");
5141 let Statement::CreateIndex(c) = s else {
5142 panic!("expected CreateIndex")
5143 };
5144 assert_eq!(c.name, "idx_id");
5145 assert_eq!(c.table, "users");
5146 assert_eq!(c.column, "id");
5147 }
5148
5149 #[test]
5150 fn create_index_missing_on_errors() {
5151 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
5152 }
5153
5154 #[test]
5155 fn create_index_missing_paren_errors() {
5156 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
5157 }
5158
5159 #[test]
5160 fn create_index_round_trip() {
5161 let original = parse("CREATE INDEX by_name ON users (name)");
5162 let again = parse_statement(&original.to_string()).unwrap();
5163 assert_eq!(original, again);
5164 }
5165
5166 #[test]
5169 fn create_unique_index_basic() {
5170 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
5171 let Statement::CreateIndex(c) = s else {
5172 panic!("expected CreateIndex");
5173 };
5174 assert!(c.is_unique);
5175 assert_eq!(c.column, "a");
5176 assert!(c.partial_predicate.is_none());
5177 }
5178
5179 #[test]
5180 fn create_unique_index_partial() {
5181 let s = parse(
5183 "CREATE UNIQUE INDEX idx_email_templates_user_default \
5184 ON email_templates (user_address) WHERE is_default = true",
5185 );
5186 let Statement::CreateIndex(c) = s else {
5187 panic!("expected CreateIndex");
5188 };
5189 assert!(c.is_unique);
5190 assert_eq!(c.table, "email_templates");
5191 assert_eq!(c.column, "user_address");
5192 assert!(c.partial_predicate.is_some());
5193 }
5194
5195 #[test]
5196 fn create_unique_index_composite_with_predicate() {
5197 let s = parse(
5199 "CREATE UNIQUE INDEX uq_calendar_events_instance \
5200 ON calendar_events (calendar_id, uid, recurrence_id) \
5201 WHERE recurrence_id IS NOT NULL",
5202 );
5203 let Statement::CreateIndex(c) = s else {
5204 panic!("expected CreateIndex");
5205 };
5206 assert!(c.is_unique);
5207 assert_eq!(c.column, "calendar_id");
5208 assert_eq!(
5209 c.extra_columns,
5210 vec!["uid".to_string(), "recurrence_id".to_string()]
5211 );
5212 assert!(c.partial_predicate.is_some());
5213 }
5214
5215 #[test]
5216 fn create_unique_index_using_btree_ok() {
5217 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
5218 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
5219 }
5220
5221 #[test]
5222 fn create_unique_index_using_hnsw_rejected() {
5223 let err =
5224 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
5225 assert!(err.message.contains("UNIQUE"), "{}", err.message);
5226 }
5227
5228 #[test]
5229 fn create_unique_index_round_trip() {
5230 let original = parse(
5231 "CREATE UNIQUE INDEX uq_calendar_events_master \
5232 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
5233 );
5234 let again = parse_statement(&original.to_string()).unwrap();
5235 assert_eq!(original, again);
5236 }
5237
5238 #[test]
5239 fn create_unique_without_index_errors() {
5240 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
5241 assert!(err.message.contains("INDEX"), "{}", err.message);
5242 }
5243
5244 #[test]
5247 fn create_table_bytea_column() {
5248 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
5249 let Statement::CreateTable(c) = s else {
5250 panic!("expected CreateTable");
5251 };
5252 assert_eq!(c.columns.len(), 2);
5253 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
5254 assert!(!c.columns[1].nullable);
5255 }
5256
5257 #[test]
5258 fn create_table_bytes_alias_column() {
5259 let s = parse("CREATE TABLE t (blob BYTES)");
5260 let Statement::CreateTable(c) = s else {
5261 panic!("expected CreateTable");
5262 };
5263 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
5264 }
5265
5266 #[test]
5267 fn bytea_round_trip_display() {
5268 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
5269 let again = parse_statement(&original.to_string()).unwrap();
5270 assert_eq!(original, again);
5271 }
5272
5273 #[test]
5276 fn begin_commit_rollback_parse_as_unit_variants() {
5277 assert_eq!(parse("BEGIN"), Statement::Begin);
5278 assert_eq!(parse("COMMIT"), Statement::Commit);
5279 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
5280 assert_eq!(parse("BEGIN;"), Statement::Begin);
5282 }
5283
5284 #[test]
5287 fn inner_product_binop_parses() {
5288 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
5289 let Statement::Select(s) = s else { panic!() };
5290 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5291 panic!()
5292 };
5293 assert!(matches!(
5294 expr,
5295 Expr::Binary {
5296 op: BinOp::InnerProduct,
5297 ..
5298 }
5299 ));
5300 }
5301
5302 #[test]
5303 fn cosine_distance_binop_parses() {
5304 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
5305 let Statement::Select(s) = s else { panic!() };
5306 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5307 panic!()
5308 };
5309 assert!(matches!(
5310 expr,
5311 Expr::Binary {
5312 op: BinOp::CosineDistance,
5313 ..
5314 }
5315 ));
5316 }
5317
5318 #[test]
5319 fn vector_cast_postfix_wraps_string_literal() {
5320 let s = parse("SELECT '[1,2,3]'::vector FROM t");
5321 let Statement::Select(s) = s else { panic!() };
5322 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5323 panic!()
5324 };
5325 assert!(matches!(
5326 expr,
5327 Expr::Cast {
5328 target: CastTarget::Vector,
5329 ..
5330 }
5331 ));
5332 }
5333
5334 #[test]
5335 fn unsupported_cast_target_errors() {
5336 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
5338 assert!(err.message.contains("unsupported cast target"));
5339 }
5340
5341 #[test]
5342 fn tx_statements_round_trip() {
5343 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
5344 let original = parse(q);
5345 let again = parse_statement(&original.to_string()).unwrap();
5346 assert_eq!(original, again);
5347 }
5348 }
5349
5350 #[test]
5351 fn interval_text_parsing_units() {
5352 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
5354 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
5355 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
5356 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
5357 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
5359 assert_eq!(
5360 parse_interval_text("1 day 2 hours"),
5361 Some((0, 86_400_000_000 + 7_200_000_000))
5362 );
5363 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
5365 assert_eq!(parse_interval_text(""), None);
5367 assert_eq!(parse_interval_text("garbage"), None);
5368 assert_eq!(parse_interval_text("1 fortnight"), None);
5369 assert_eq!(parse_interval_text("1"), None);
5370 }
5371
5372 #[test]
5373 fn interval_literal_roundtrips_via_display() {
5374 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
5375 let s = parsed.to_string();
5376 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
5378 let again = parse_statement(&s).unwrap();
5380 assert_eq!(parsed, again);
5381 }
5382
5383 #[test]
5386 fn parser_recognises_create_publication_bare() {
5387 let s = parse("CREATE PUBLICATION pub_a");
5388 let Statement::CreatePublication(p) = s else {
5389 panic!("expected CreatePublication, got {s:?}")
5390 };
5391 assert_eq!(p.name, "pub_a");
5392 assert_eq!(p.scope, PublicationScope::AllTables);
5393 }
5394
5395 #[test]
5396 fn parser_recognises_create_publication_for_all_tables() {
5397 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
5398 let Statement::CreatePublication(p) = s else {
5399 panic!("expected CreatePublication, got {s:?}")
5400 };
5401 assert_eq!(p.name, "pub_a");
5402 assert_eq!(p.scope, PublicationScope::AllTables);
5403 }
5404
5405 #[test]
5406 fn parser_recognises_drop_publication() {
5407 let s = parse("DROP PUBLICATION pub_a");
5408 let Statement::DropPublication(name) = s else {
5409 panic!("expected DropPublication, got {s:?}")
5410 };
5411 assert_eq!(name, "pub_a");
5412 }
5413
5414 #[test]
5415 fn parser_recognises_for_table_list() {
5416 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
5417 let Statement::CreatePublication(p) = s else {
5418 panic!("expected CreatePublication, got {s:?}")
5419 };
5420 assert_eq!(p.name, "pub_a");
5421 let PublicationScope::ForTables(ts) = p.scope else {
5422 panic!("expected ForTables scope")
5423 };
5424 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
5425 }
5426
5427 #[test]
5428 fn parser_recognises_for_tables_plural() {
5429 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
5431 let Statement::CreatePublication(p) = s else {
5432 panic!("expected CreatePublication, got {s:?}")
5433 };
5434 let PublicationScope::ForTables(ts) = p.scope else {
5435 panic!("expected ForTables")
5436 };
5437 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5438 }
5439
5440 #[test]
5441 fn parser_recognises_for_all_tables_except_list() {
5442 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
5443 let Statement::CreatePublication(p) = s else {
5444 panic!()
5445 };
5446 let PublicationScope::AllTablesExcept(ts) = p.scope else {
5447 panic!("expected AllTablesExcept")
5448 };
5449 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5450 }
5451
5452 #[test]
5453 fn parser_rejects_for_table_with_empty_list() {
5454 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
5456 .expect_err("must error on empty list");
5457 assert!(!err.message.is_empty());
5460 }
5461
5462 #[test]
5463 fn parser_recognises_show_publications() {
5464 let s = parse("SHOW PUBLICATIONS");
5467 assert!(matches!(s, Statement::ShowPublications));
5468 }
5469
5470 #[test]
5473 fn parser_recognises_create_subscription_single_publication() {
5474 let s = parse(
5475 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
5476 );
5477 let Statement::CreateSubscription(c) = s else {
5478 panic!("expected CreateSubscription, got {s:?}")
5479 };
5480 assert_eq!(c.name, "sub_a");
5481 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
5482 assert_eq!(c.publications, alloc::vec!["pub_a"]);
5483 }
5484
5485 #[test]
5486 fn parser_recognises_create_subscription_multi_publication() {
5487 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
5488 let Statement::CreateSubscription(c) = s else {
5489 panic!()
5490 };
5491 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
5492 }
5493
5494 #[test]
5495 fn parser_rejects_create_subscription_missing_connection() {
5496 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
5497 .expect_err("must error on missing CONNECTION");
5498 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
5499 }
5500
5501 #[test]
5502 fn parser_rejects_create_subscription_missing_publication() {
5503 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
5504 .expect_err("must error on missing PUBLICATION");
5505 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
5506 }
5507
5508 #[test]
5509 fn parser_recognises_drop_subscription() {
5510 let s = parse("DROP SUBSCRIPTION sub_a");
5511 let Statement::DropSubscription(name) = s else {
5512 panic!("expected DropSubscription, got {s:?}")
5513 };
5514 assert_eq!(name, "sub_a");
5515 }
5516
5517 #[test]
5518 fn parser_recognises_show_subscriptions() {
5519 let s = parse("SHOW SUBSCRIPTIONS");
5520 assert!(matches!(s, Statement::ShowSubscriptions));
5521 }
5522
5523 #[test]
5524 fn parser_recognises_wait_for_wal_position_no_timeout() {
5525 let s = parse("WAIT FOR WAL POSITION 12345");
5526 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
5527 panic!("expected WaitForWalPosition, got {s:?}")
5528 };
5529 assert_eq!(pos, 12345);
5530 assert!(timeout_ms.is_none());
5531 }
5532
5533 #[test]
5534 fn parser_recognises_wait_for_wal_position_with_timeout() {
5535 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
5536 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
5537 panic!()
5538 };
5539 assert_eq!(pos, 67890);
5540 assert_eq!(timeout_ms, Some(5000));
5541 }
5542
5543 #[test]
5544 fn parser_rejects_wait_with_negative_position() {
5545 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
5551 assert!(!err.message.is_empty());
5552 }
5553
5554 #[test]
5555 fn parser_recognises_bare_analyze() {
5556 let s = parse("ANALYZE");
5557 assert!(matches!(s, Statement::Analyze(None)));
5558 }
5559
5560 #[test]
5561 fn parser_recognises_analyze_with_table() {
5562 let s = parse("ANALYZE users");
5563 let Statement::Analyze(Some(name)) = s else {
5564 panic!("expected Analyze, got {s:?}")
5565 };
5566 assert_eq!(name, "users");
5567 }
5568
5569 #[test]
5570 fn parser_recognises_analyze_with_quoted_table() {
5571 let s = parse("ANALYZE \"Mixed Case\"");
5572 let Statement::Analyze(Some(name)) = s else {
5573 panic!()
5574 };
5575 assert_eq!(name, "Mixed Case");
5576 }
5577
5578 #[test]
5579 fn parser_rejects_analyze_with_garbage_token() {
5580 let err = parse_statement("ANALYZE 42").expect_err("must error");
5581 assert!(!err.message.is_empty());
5582 }
5583
5584 #[test]
5585 fn analyze_display_roundtrips() {
5586 for sql in ["ANALYZE", "ANALYZE users"] {
5587 let s = parse(sql);
5588 let printed = s.to_string();
5589 let again = parse_statement(&printed)
5590 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
5591 assert_eq!(s, again);
5592 }
5593 }
5594
5595 #[test]
5596 fn wait_for_display_roundtrips() {
5597 for sql in [
5598 "WAIT FOR WAL POSITION 12345",
5599 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
5600 ] {
5601 let s = parse(sql);
5602 let printed = s.to_string();
5603 let again = parse_statement(&printed)
5604 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
5605 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
5606 }
5607 }
5608
5609 #[test]
5610 fn subscription_ddl_display_roundtrips() {
5611 for sql in [
5612 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
5613 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
5614 "DROP SUBSCRIPTION sub_a",
5615 "SHOW SUBSCRIPTIONS",
5616 ] {
5617 let s = parse(sql);
5618 let printed = s.to_string();
5619 let again = parse_statement(&printed)
5620 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
5621 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
5622 }
5623 }
5624
5625 #[test]
5626 fn parser_drop_dispatches_user_vs_publication() {
5627 let s = parse("DROP USER 'alice'");
5630 let Statement::DropUser(name) = s else {
5631 panic!("expected DropUser, got {s:?}")
5632 };
5633 assert_eq!(name, "alice");
5634 let s = parse("DROP PUBLICATION p1");
5636 assert!(matches!(s, Statement::DropPublication(_)));
5637 }
5638
5639 #[test]
5640 fn publication_ddl_display_roundtrips() {
5641 for sql in [
5644 "CREATE PUBLICATION pub_a",
5645 "CREATE PUBLICATION pub_a FOR ALL TABLES",
5646 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
5647 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
5648 "DROP PUBLICATION pub_a",
5649 "SHOW PUBLICATIONS",
5650 ] {
5651 let s = parse(sql);
5652 let printed = s.to_string();
5653 let again = parse_statement(&printed)
5654 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
5655 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
5656 }
5657 }
5658
5659 #[test]
5662 fn create_function_returns_trigger_plpgsql_minimal() {
5663 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
5664 let s = parse(sql);
5665 let Statement::CreateFunction(f) = s else {
5666 panic!("expected CreateFunction");
5667 };
5668 assert_eq!(f.name, "noop");
5669 assert!(!f.or_replace);
5670 assert!(f.args.is_empty());
5671 assert!(matches!(f.returns, FunctionReturn::Trigger));
5672 assert_eq!(f.language, "plpgsql");
5673 let FunctionBody::PlPgSql(block) = f.body else {
5674 panic!("expected PlPgSql body");
5675 };
5676 assert_eq!(block.statements.len(), 1);
5677 assert!(matches!(
5678 block.statements[0],
5679 PlPgSqlStmt::Return(ReturnTarget::New)
5680 ));
5681 }
5682
5683 #[test]
5684 fn create_function_or_replace_with_assignment() {
5685 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
5688BEGIN
5689 NEW.search_vector := to_tsvector('english', NEW.subject);
5690 RETURN NEW;
5691END;
5692$$";
5693 let s = parse(sql);
5694 let Statement::CreateFunction(f) = s else {
5695 panic!("expected CreateFunction");
5696 };
5697 assert!(f.or_replace);
5698 let FunctionBody::PlPgSql(block) = &f.body else {
5699 panic!("expected PlPgSql body");
5700 };
5701 assert_eq!(block.statements.len(), 2);
5702 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
5704 panic!("expected Assign as first stmt");
5705 };
5706 match target {
5707 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
5708 other => panic!("expected NEW.col, got {other:?}"),
5709 }
5710 assert!(matches!(
5712 block.statements[1],
5713 PlPgSqlStmt::Return(ReturnTarget::New)
5714 ));
5715 }
5716
5717 #[test]
5718 fn create_trigger_after_insert_or_update() {
5719 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
5720 let s = parse(sql);
5721 let Statement::CreateTrigger(t) = s else {
5722 panic!("expected CreateTrigger");
5723 };
5724 assert_eq!(t.name, "tg");
5725 assert_eq!(t.table, "messages");
5726 assert_eq!(t.timing, TriggerTiming::After);
5727 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
5728 assert_eq!(t.for_each, TriggerForEach::Row);
5729 assert_eq!(t.function, "update_sv");
5730 }
5731
5732 #[test]
5733 fn create_trigger_before_delete_execute_procedure_alias() {
5734 let sql =
5736 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
5737 let s = parse(sql);
5738 let Statement::CreateTrigger(t) = s else {
5739 panic!("expected CreateTrigger");
5740 };
5741 assert_eq!(t.timing, TriggerTiming::Before);
5742 assert_eq!(t.events, vec![TriggerEvent::Delete]);
5743 }
5744
5745 #[test]
5746 fn drop_trigger_if_exists_round_trips() {
5747 let s = Statement::DropTrigger {
5752 name: "tg".into(),
5753 table: "messages".into(),
5754 if_exists: true,
5755 };
5756 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
5757 }
5758
5759 #[test]
5760 fn trigger_ddl_display_roundtrips_through_parser() {
5761 for sql in [
5765 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
5766 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
5767 ] {
5768 let s = parse(sql);
5769 let printed = s.to_string();
5770 let again = parse_statement(&printed)
5771 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
5772 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
5773 }
5774 }
5775}