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 {
40 let lc = name.to_ascii_lowercase();
41 matches!(
42 lc.as_str(),
43 "vector_cosine_ops"
44 | "vector_l2_ops"
45 | "vector_ip_ops"
46 | "halfvec_cosine_ops"
47 | "halfvec_l2_ops"
48 | "halfvec_ip_ops"
49 | "sq8_cosine_ops"
50 | "sq8_l2_ops"
51 | "sq8_ip_ops"
52 | "gin_trgm_ops"
58 | "gist_trgm_ops"
59 | "text_pattern_ops"
64 | "varchar_pattern_ops"
65 | "bpchar_pattern_ops"
66 | "int4_ops"
67 | "int8_ops"
68 | "text_ops"
69 )
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct ParseError {
74 pub message: String,
75 pub token_pos: usize,
77}
78
79impl fmt::Display for ParseError {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(
82 f,
83 "parse error at token #{}: {}",
84 self.token_pos, self.message
85 )
86 }
87}
88
89impl From<LexError> for ParseError {
90 fn from(e: LexError) -> Self {
91 Self {
92 message: format!("lex: {e}"),
93 token_pos: 0,
94 }
95 }
96}
97
98pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
104 let tokens = lexer::tokenize(input)?;
105 let mut p = Parser::new(tokens);
106 let expr = p.parse_expr(0)?;
107 p.expect_eof()?;
108 Ok(expr)
109}
110
111pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
114 let tokens = lexer::tokenize(input)?;
115 let mut p = Parser::new(tokens);
116 let stmt = p.parse_one_statement()?;
117 if matches!(p.peek(), Token::Semicolon) {
118 p.advance();
119 }
120 p.expect_eof()?;
121 Ok(stmt)
122}
123
124struct Parser {
125 tokens: Vec<Token>,
126 pos: usize,
127}
128
129impl Parser {
130 fn new(tokens: Vec<Token>) -> Self {
131 Self { tokens, pos: 0 }
132 }
133
134 fn peek(&self) -> &Token {
135 &self.tokens[self.pos]
137 }
138
139 fn advance(&mut self) -> Token {
140 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
141 if self.pos + 1 < self.tokens.len() {
142 self.pos += 1;
143 }
144 t
145 }
146
147 fn err(&self, message: String) -> ParseError {
148 ParseError {
149 message,
150 token_pos: self.pos,
151 }
152 }
153
154 fn expect_eof(&self) -> Result<(), ParseError> {
155 if matches!(self.peek(), Token::Eof) {
156 Ok(())
157 } else {
158 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
159 }
160 }
161
162 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
163 match self.advance() {
164 Token::Ident(s) | Token::QuotedIdent(s) => Ok(s),
165 other => Err(ParseError {
166 message: format!("expected identifier, got {other:?}"),
167 token_pos: self.pos.saturating_sub(1),
168 }),
169 }
170 }
171
172 #[allow(clippy::too_many_lines)]
173 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
174 match self.peek() {
175 Token::Select => self.parse_select_stmt(),
176 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
182 self.advance();
183 match self.advance() {
186 Token::String(_) => {}
187 other => {
188 return Err(self.err(alloc::format!(
189 "expected dollar-quoted body after DO, got {other:?}"
190 )));
191 }
192 }
193 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
195 self.advance();
196 let _ = self.expect_ident_like()?;
197 }
198 Ok(Statement::DoBlock)
199 }
200 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
204 self.advance();
205 self.parse_with_cte_then_select()
206 }
207 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
210 self.advance();
211 let mut analyze = false;
212 let mut suggest = false;
213 if matches!(self.peek(), Token::LParen) {
215 self.advance();
216 let opt = match self.peek().clone() {
217 Token::Ident(s) | Token::QuotedIdent(s) => s,
218 other => {
219 return Err(self.err(format!(
220 "expected option keyword inside EXPLAIN (…), got {other:?}"
221 )));
222 }
223 };
224 if !opt.eq_ignore_ascii_case("suggest") {
225 return Err(self.err(format!(
226 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
227 )));
228 }
229 self.advance();
230 if !matches!(self.peek(), Token::RParen) {
231 return Err(self.err(format!(
232 "expected ')' after EXPLAIN option, got {:?}",
233 self.peek()
234 )));
235 }
236 self.advance();
237 suggest = true;
238 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
239 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
240 {
241 self.advance();
242 analyze = true;
243 }
244 let inner = self.parse_select_stmt()?;
245 let Statement::Select(s) = inner else {
246 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
247 };
248 Ok(Statement::Explain(crate::ast::ExplainStatement {
249 analyze,
250 inner: Box::new(s),
251 suggest,
252 }))
253 }
254 Token::Create => self.parse_create_stmt(),
255 Token::Insert => self.parse_insert_stmt(),
256 Token::Begin => {
257 self.advance();
258 Ok(Statement::Begin)
259 }
260 Token::Commit => {
261 self.advance();
262 Ok(Statement::Commit)
263 }
264 Token::Rollback => {
265 self.advance();
266 if matches!(self.peek(), Token::To) {
270 self.advance();
271 if matches!(self.peek(), Token::Savepoint) {
272 self.advance();
273 }
274 let name = self.expect_ident_like()?;
275 Ok(Statement::RollbackToSavepoint(name))
276 } else {
277 Ok(Statement::Rollback)
278 }
279 }
280 Token::Savepoint => {
281 self.advance();
282 let name = self.expect_ident_like()?;
283 Ok(Statement::Savepoint(name))
284 }
285 Token::Release => {
286 self.advance();
287 if matches!(self.peek(), Token::Savepoint) {
290 self.advance();
291 }
292 let name = self.expect_ident_like()?;
293 Ok(Statement::ReleaseSavepoint(name))
294 }
295 Token::Show => {
296 self.advance();
297 let target = match self.advance() {
303 Token::Tables => "tables".to_string(),
304 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
305 other => {
306 return Err(self.err(format!(
307 "expected SHOW target, got {other:?}"
308 )));
309 }
310 };
311 match target.as_str() {
312 "tables" => Ok(Statement::ShowTables),
313 "users" => Ok(Statement::ShowUsers),
314 "publications" => Ok(Statement::ShowPublications),
319 "subscriptions" => Ok(Statement::ShowSubscriptions),
321 "columns" => {
322 if !matches!(self.peek(), Token::From) {
323 return Err(self.err(format!(
324 "expected FROM after SHOW COLUMNS, got {:?}",
325 self.peek()
326 )));
327 }
328 self.advance();
329 let table = self.expect_ident_like()?;
330 Ok(Statement::ShowColumns(table))
331 }
332 other => Err(self.err(format!(
333 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
334 ))),
335 }
336 }
337 Token::Drop => {
343 self.advance();
344 match self.peek() {
345 Token::Publication => {
346 self.advance();
347 let name = self.expect_ident_or_string()?;
348 Ok(Statement::DropPublication(name))
349 }
350 Token::Subscription => {
351 self.advance();
352 let name = self.expect_ident_or_string()?;
353 Ok(Statement::DropSubscription(name))
354 }
355 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
356 self.advance();
357 let name = self.expect_ident_or_string()?;
358 Ok(Statement::DropUser(name))
359 }
360 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
362 self.advance();
363 let if_exists = self.consume_if_exists();
364 let name = self.expect_ident_like()?;
365 if !matches!(self.peek(), Token::On) {
367 return Err(self.err(alloc::format!(
368 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
369 self.peek()
370 )));
371 }
372 self.advance();
373 let table = self.expect_ident_like()?;
374 Ok(Statement::DropTrigger {
375 name,
376 table,
377 if_exists,
378 })
379 }
380 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
384 self.advance();
385 let if_exists = self.consume_if_exists();
386 let name = self.expect_ident_like()?;
387 if matches!(self.peek(), Token::LParen) {
389 self.advance();
390 let mut depth = 1usize;
392 while depth > 0 {
393 match self.peek() {
394 Token::LParen => depth += 1,
395 Token::RParen => depth -= 1,
396 Token::Eof => {
397 return Err(self.err(alloc::format!(
398 "unterminated arg list in DROP FUNCTION {name:?}"
399 )));
400 }
401 _ => {}
402 }
403 self.advance();
404 }
405 }
406 Ok(Statement::DropFunction { name, if_exists })
407 }
408 other => Err(self.err(format!(
409 "expected USER / PUBLICATION / SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
410 ))),
411 }
412 }
413 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
414 self.advance();
415 self.parse_update_after_keyword()
416 }
417 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
418 self.advance();
419 self.parse_delete_after_keyword()
420 }
421 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
425 self.advance();
426 self.parse_alter_after_keyword()
427 }
428 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
432 self.advance();
433 self.parse_wait_after_keyword()
434 }
435 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
446 self.advance();
447 let next = self.peek().clone();
448 let cold = match next {
449 Token::Ident(s) | Token::QuotedIdent(s) => s,
450 _ => {
451 return Err(
452 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
453 );
454 }
455 };
456 if !cold.eq_ignore_ascii_case("cold") {
457 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
458 }
459 self.advance();
460 let next = self.peek().clone();
461 let segments = match next {
462 Token::Ident(s) | Token::QuotedIdent(s) => s,
463 _ => {
464 return Err(self.err(format!(
465 "expected SEGMENTS after COMPACT COLD, got {:?}",
466 self.peek()
467 )));
468 }
469 };
470 if !segments.eq_ignore_ascii_case("segments") {
471 return Err(self.err(format!(
472 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
473 )));
474 }
475 self.advance();
476 Ok(Statement::CompactColdSegments)
477 }
478 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
479 self.advance();
480 let target = match self.peek() {
481 Token::Eof | Token::Semicolon => None,
482 Token::Ident(_) | Token::QuotedIdent(_) => {
483 Some(self.expect_ident_like()?)
484 }
485 other => {
486 return Err(self.err(format!(
487 "expected table name or end of statement after ANALYZE, got {other:?}"
488 )));
489 }
490 };
491 Ok(Statement::Analyze(target))
492 }
493 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
499 self.advance();
500 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("local") || s.eq_ignore_ascii_case("session"))
503 {
504 self.advance();
505 }
506 let name = self.parse_set_param_name()?;
507 match self.peek() {
509 Token::Eq => {
510 self.advance();
511 }
512 Token::To => {
513 self.advance();
514 }
515 other => {
516 return Err(self.err(format!(
517 "expected `=` or TO after SET {name}, got {other:?}"
518 )));
519 }
520 }
521 let value = self.parse_set_value()?;
522 Ok(Statement::SetParameter { name, value })
523 }
524 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
526 self.advance();
527 match self.peek().clone() {
528 Token::All => {
529 self.advance();
530 Ok(Statement::ResetParameter(None))
531 }
532 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
533 self.advance();
534 Ok(Statement::ResetParameter(None))
535 }
536 _ => {
537 let name = self.parse_set_param_name()?;
538 Ok(Statement::ResetParameter(Some(name)))
539 }
540 }
541 }
542 other => Err(self.err(format!(
543 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
544 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
545 ))),
546 }
547 }
548
549 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
550 debug_assert!(matches!(self.peek(), Token::Create));
551 self.advance();
552 match self.peek() {
553 Token::Table => self.parse_create_table_stmt_after_create(),
554 Token::Index => self.parse_create_index_stmt_after_create(false),
555 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
562 self.advance();
563 if !matches!(self.peek(), Token::Index) {
564 return Err(self.err(alloc::format!(
565 "expected INDEX after CREATE UNIQUE, got {:?}",
566 self.peek()
567 )));
568 }
569 self.parse_create_index_stmt_after_create(true)
570 }
571 Token::Publication => {
572 self.advance();
573 self.parse_create_publication_after_keyword()
574 }
575 Token::Subscription => {
576 self.advance();
577 self.parse_create_subscription_after_keyword()
578 }
579 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
583 self.advance();
584 self.parse_create_user_after_keyword()
585 }
586 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
590 self.advance();
591 self.parse_create_extension_after_keyword()
592 }
593 Token::Or => {
599 self.advance();
600 let next = self.peek();
601 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
602 return Err(self.err(alloc::format!(
603 "expected REPLACE after CREATE OR, got {next:?}"
604 )));
605 };
606 if !s2.eq_ignore_ascii_case("replace") {
607 return Err(self.err(alloc::format!(
608 "expected REPLACE after CREATE OR, got {s2:?}"
609 )));
610 }
611 self.advance();
612 self.parse_create_function_or_trigger_after_or_replace(true)
613 }
614 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
615 self.advance();
616 self.parse_create_function_after_keyword(false)
617 }
618 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
619 self.advance();
620 self.parse_create_trigger_after_keyword(false)
621 }
622 other => Err(self.err(format!(
623 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER [OR REPLACE …] after CREATE, got {other:?}"
624 ))),
625 }
626 }
627
628 fn parse_create_function_or_trigger_after_or_replace(
633 &mut self,
634 or_replace: bool,
635 ) -> Result<Statement, ParseError> {
636 let tok = self.peek();
637 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
638 return Err(self.err(alloc::format!(
639 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {tok:?}"
640 )));
641 };
642 if s.eq_ignore_ascii_case("function") {
643 self.advance();
644 self.parse_create_function_after_keyword(or_replace)
645 } else if s.eq_ignore_ascii_case("trigger") {
646 self.advance();
647 self.parse_create_trigger_after_keyword(or_replace)
648 } else {
649 Err(self.err(alloc::format!(
650 "expected FUNCTION / TRIGGER after CREATE OR REPLACE, got {s:?}"
651 )))
652 }
653 }
654
655 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
660 self.consume_if_not_exists();
662 let name = self.expect_ident_like()?;
663 loop {
666 match self.peek() {
667 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
668 self.advance();
669 continue;
670 }
671 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
672 self.advance();
673 let _ = self.expect_ident_like()?;
674 continue;
675 }
676 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
677 self.advance();
678 let _ = self.advance();
680 continue;
681 }
682 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
683 self.advance();
684 let _ = self.advance();
685 continue;
686 }
687 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
688 self.advance();
689 continue;
690 }
691 _ => break,
692 }
693 }
694 Ok(Statement::CreateExtension(name))
695 }
696
697 fn parse_create_function_after_keyword(
709 &mut self,
710 or_replace: bool,
711 ) -> Result<Statement, ParseError> {
712 let name = self.expect_ident_like()?;
713 if !matches!(self.peek(), Token::LParen) {
717 return Err(self.err(alloc::format!(
718 "expected '(' after function name {name:?}, got {:?}",
719 self.peek()
720 )));
721 }
722 self.advance();
723 let args = self.parse_function_arg_list()?;
724 let tok = self.peek();
726 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
727 return Err(self.err(alloc::format!(
728 "expected RETURNS after function arg list, got {tok:?}"
729 )));
730 };
731 if !s.eq_ignore_ascii_case("returns") {
732 return Err(self.err(alloc::format!(
733 "expected RETURNS after function arg list, got {s:?}"
734 )));
735 }
736 self.advance();
737 let returns = self.parse_function_return()?;
738 let mut language: Option<String> = self.parse_optional_language()?;
741 if !matches!(self.peek(), Token::As) {
745 return Err(self.err(alloc::format!(
746 "expected AS before function body, got {:?}",
747 self.peek()
748 )));
749 }
750 self.advance();
751 let body_text = match self.peek() {
752 Token::String(s) => {
753 let body = s.clone();
754 self.advance();
755 body
756 }
757 other => {
758 return Err(self.err(alloc::format!(
759 "expected $$-quoted function body after AS, got {other:?}"
760 )));
761 }
762 };
763 if language.is_none() {
765 language = self.parse_optional_language()?;
766 }
767 let language = language.unwrap_or_else(|| String::from("sql"));
768 let body = if language.eq_ignore_ascii_case("plpgsql") {
773 match parse_plpgsql_body(&body_text) {
774 Ok(block) => FunctionBody::PlPgSql(block),
775 Err(_) => FunctionBody::Raw(body_text),
781 }
782 } else {
783 FunctionBody::Raw(body_text)
784 };
785 Ok(Statement::CreateFunction(CreateFunctionStatement {
786 name,
787 or_replace,
788 args,
789 returns,
790 language,
791 body,
792 }))
793 }
794
795 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
799 let mut args: Vec<FunctionArg> = Vec::new();
800 if matches!(self.peek(), Token::RParen) {
801 self.advance();
802 return Ok(args);
803 }
804 loop {
805 let mode = if matches!(self.peek(), Token::In) {
808 self.advance();
809 FunctionArgMode::In
810 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
811 {
812 self.advance();
813 FunctionArgMode::Out
814 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
815 {
816 self.advance();
817 FunctionArgMode::InOut
818 } else {
819 FunctionArgMode::In
820 };
821 let (name, ty_token) = {
827 let first = self.expect_ident_like()?;
828 match self.peek() {
831 Token::Ident(_) | Token::QuotedIdent(_) => {
832 let ty = self.expect_ident_like()?;
833 (Some(first), ty)
834 }
835 _ => (None, first),
836 }
837 };
838 let ty = match map_type_ident_to_column_type_name(&ty_token) {
840 Some(t) => FunctionArgType::Typed(t),
841 None => FunctionArgType::Raw(ty_token),
842 };
843 args.push(FunctionArg { mode, name, ty });
844 match self.peek() {
845 Token::Comma => {
846 self.advance();
847 continue;
848 }
849 Token::RParen => {
850 self.advance();
851 return Ok(args);
852 }
853 other => {
854 return Err(self.err(alloc::format!(
855 "expected , or ) in function arg list, got {other:?}"
856 )));
857 }
858 }
859 }
860 }
861
862 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
863 let ident = self.expect_ident_like()?;
864 if ident.eq_ignore_ascii_case("trigger") {
865 return Ok(FunctionReturn::Trigger);
866 }
867 if ident.eq_ignore_ascii_case("void") {
868 return Ok(FunctionReturn::Void);
869 }
870 match map_type_ident_to_column_type_name(&ident) {
871 Some(t) => Ok(FunctionReturn::Type(t)),
872 None => Ok(FunctionReturn::Other(ident)),
873 }
874 }
875
876 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
877 match self.peek() {
878 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
879 self.advance();
880 let lang = self.expect_ident_like()?;
881 Ok(Some(lang.to_ascii_lowercase()))
882 }
883 _ => Ok(None),
884 }
885 }
886
887 fn parse_create_trigger_after_keyword(
891 &mut self,
892 or_replace: bool,
893 ) -> Result<Statement, ParseError> {
894 let name = self.expect_ident_like()?;
895 let timing = {
896 let ident = self.expect_ident_like()?;
897 if ident.eq_ignore_ascii_case("before") {
898 TriggerTiming::Before
899 } else if ident.eq_ignore_ascii_case("after") {
900 TriggerTiming::After
901 } else if ident.eq_ignore_ascii_case("instead") {
902 let next = self.expect_ident_like()?;
903 if !next.eq_ignore_ascii_case("of") {
904 return Err(self.err(alloc::format!(
905 "expected OF after INSTEAD in trigger timing, got {next:?}"
906 )));
907 }
908 TriggerTiming::InsteadOf
909 } else {
910 return Err(self.err(alloc::format!(
911 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
912 )));
913 }
914 };
915 let mut events: Vec<TriggerEvent> = Vec::new();
922 let mut update_columns: Vec<String> = Vec::new();
923 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
924 events.push(first_ev);
925 if !first_cols.is_empty() {
926 update_columns = first_cols;
927 }
928 while matches!(self.peek(), Token::Or) {
929 self.advance();
930 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
931 events.push(ev);
932 if !cols.is_empty() {
933 if !update_columns.is_empty() {
934 return Err(self.err(
935 "CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into(),
936 ));
937 }
938 update_columns = cols;
939 }
940 }
941 let tok = self.peek();
943 let Token::On = tok else {
944 return Err(self.err(alloc::format!(
945 "expected ON after trigger events, got {tok:?}"
946 )));
947 };
948 self.advance();
949 let table = self.expect_ident_like()?;
950 if !matches!(self.peek(), Token::For) {
954 return Err(self.err(alloc::format!(
955 "expected FOR EACH ROW / STATEMENT, got {:?}",
956 self.peek()
957 )));
958 }
959 self.advance();
960 let for_each = {
961 let e = self.expect_ident_like()?;
962 if !e.eq_ignore_ascii_case("each") {
963 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
964 }
965 let unit = self.expect_ident_like()?;
966 if unit.eq_ignore_ascii_case("row") {
967 TriggerForEach::Row
968 } else if unit.eq_ignore_ascii_case("statement") {
969 TriggerForEach::Statement
970 } else {
971 return Err(self.err(alloc::format!(
972 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
973 )));
974 }
975 };
976 let exec = self.expect_ident_like()?;
978 if !exec.eq_ignore_ascii_case("execute") {
979 return Err(self.err(alloc::format!(
980 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
981 )));
982 }
983 let fn_or_proc = self.expect_ident_like()?;
984 if !(fn_or_proc.eq_ignore_ascii_case("function")
985 || fn_or_proc.eq_ignore_ascii_case("procedure"))
986 {
987 return Err(self.err(alloc::format!(
988 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
989 )));
990 }
991 let function = self.expect_ident_like()?;
992 if matches!(self.peek(), Token::LParen) {
994 self.advance();
995 if !matches!(self.peek(), Token::RParen) {
996 return Err(self.err(alloc::format!(
997 "v7.12.4 trigger function calls take no args; got {:?}",
998 self.peek()
999 )));
1000 }
1001 self.advance();
1002 }
1003 Ok(Statement::CreateTrigger(CreateTriggerStatement {
1004 name,
1005 or_replace,
1006 timing,
1007 events,
1008 table,
1009 for_each,
1010 function,
1011 update_columns,
1012 }))
1013 }
1014
1015 fn parse_trigger_event_with_optional_of(
1019 &mut self,
1020 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
1021 let ev = self.parse_trigger_event()?;
1022 if !matches!(ev, TriggerEvent::Update) {
1023 return Ok((ev, Vec::new()));
1024 }
1025 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
1027 return Ok((ev, Vec::new()));
1028 }
1029 self.advance(); let mut cols: Vec<String> = Vec::new();
1031 loop {
1032 cols.push(self.expect_ident_like()?);
1033 if matches!(self.peek(), Token::Comma) {
1034 self.advance();
1035 continue;
1036 }
1037 break;
1038 }
1039 if cols.is_empty() {
1040 return Err(self.err(
1041 "CREATE TRIGGER: `UPDATE OF` requires at least one column name".into(),
1042 ));
1043 }
1044 Ok((ev, cols))
1045 }
1046
1047 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
1054 let declarations = if matches!(
1056 self.peek(),
1057 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
1058 ) {
1059 self.advance();
1060 self.parse_plpgsql_declare_block()?
1061 } else {
1062 Vec::new()
1063 };
1064 if !matches!(self.peek(), Token::Begin) {
1069 return Err(self.err(alloc::format!(
1070 "expected BEGIN at start of plpgsql block, got {:?}",
1071 self.peek()
1072 )));
1073 }
1074 self.advance();
1075 let statements = self.parse_plpgsql_stmt_list_until_end()?;
1076 Ok(PlPgSqlBlock {
1077 declarations,
1078 statements,
1079 })
1080 }
1081
1082 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
1086 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
1087 loop {
1088 if matches!(self.peek(), Token::Begin) {
1089 return Ok(out);
1090 }
1091 let name = self.expect_ident_like()?;
1092 let ty_token = self.expect_ident_like()?;
1093 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1094 Some(t) => FunctionArgType::Typed(t),
1095 None => FunctionArgType::Raw(ty_token),
1096 };
1097 let default = match self.peek() {
1098 Token::ColonEq => {
1099 self.advance();
1100 Some(self.parse_expr(0)?)
1101 }
1102 Token::Eq => {
1103 self.advance();
1107 Some(self.parse_expr(0)?)
1108 }
1109 _ => None,
1110 };
1111 if !matches!(self.peek(), Token::Semicolon) {
1113 return Err(self.err(alloc::format!(
1114 "expected ; after DECLARE entry for {name:?}, got {:?}",
1115 self.peek()
1116 )));
1117 }
1118 self.advance();
1119 out.push(PlPgSqlDeclare { name, ty, default });
1120 }
1121 }
1122
1123 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
1128 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
1129 loop {
1130 while matches!(self.peek(), Token::Semicolon) {
1132 self.advance();
1133 }
1134 if matches!(
1136 self.peek(),
1137 Token::Ident(s) | Token::QuotedIdent(s)
1138 if s.eq_ignore_ascii_case("end")
1139 || s.eq_ignore_ascii_case("else")
1140 || s.eq_ignore_ascii_case("elsif")
1141 || s.eq_ignore_ascii_case("elseif")
1142 ) {
1143 return Ok(statements);
1144 }
1145 let stmt = self.parse_plpgsql_stmt()?;
1148 statements.push(stmt);
1149 match self.peek() {
1150 Token::Semicolon => {
1151 self.advance();
1152 }
1153 Token::Ident(s) | Token::QuotedIdent(s)
1154 if s.eq_ignore_ascii_case("end")
1155 || s.eq_ignore_ascii_case("else")
1156 || s.eq_ignore_ascii_case("elsif")
1157 || s.eq_ignore_ascii_case("elseif") =>
1158 {
1159 }
1161 other => {
1162 return Err(self.err(alloc::format!(
1163 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
1164 )));
1165 }
1166 }
1167 }
1168 }
1169
1170 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1171 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
1173 {
1174 self.advance();
1175 return self.parse_plpgsql_return();
1176 }
1177 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
1179 {
1180 self.advance();
1181 return self.parse_plpgsql_if();
1182 }
1183 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
1185 {
1186 self.advance();
1187 return self.parse_plpgsql_raise();
1188 }
1189 if matches!(self.peek(), Token::Insert)
1195 || matches!(self.peek(), Token::Select)
1196 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("delete"))
1197 {
1198 let stmt = self.parse_one_statement()?;
1199 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
1200 }
1201 let target = self.parse_plpgsql_assign_target()?;
1204 match self.peek() {
1207 Token::ColonEq => {
1208 self.advance();
1209 }
1210 Token::Colon => {
1211 self.advance();
1212 if !matches!(self.peek(), Token::Eq) {
1213 return Err(self.err(alloc::format!(
1214 "expected := after plpgsql assign target, got `:` then {:?}",
1215 self.peek()
1216 )));
1217 }
1218 self.advance();
1219 }
1220 other => {
1221 return Err(self.err(alloc::format!(
1222 "expected := after plpgsql assign target, got {other:?}"
1223 )));
1224 }
1225 }
1226 let value = self.parse_expr(0)?;
1227 Ok(PlPgSqlStmt::Assign { target, value })
1228 }
1229
1230 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1233 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
1234 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
1235 loop {
1236 let cond = self.parse_expr(0)?;
1238 let then_kw = self.expect_ident_like()?;
1239 if !then_kw.eq_ignore_ascii_case("then") {
1240 return Err(self.err(alloc::format!(
1241 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
1242 )));
1243 }
1244 let body = self.parse_plpgsql_stmt_list_until_end()?;
1245 branches.push((cond, body));
1246 match self.peek() {
1248 Token::Ident(s) | Token::QuotedIdent(s)
1249 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
1250 {
1251 self.advance();
1252 continue;
1253 }
1254 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
1255 self.advance();
1256 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
1257 break;
1258 }
1259 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
1260 break;
1261 }
1262 other => {
1263 return Err(self.err(alloc::format!(
1264 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
1265 )));
1266 }
1267 }
1268 }
1269 let end_kw = self.expect_ident_like()?;
1272 if !end_kw.eq_ignore_ascii_case("end") {
1273 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
1274 }
1275 let if_kw = self.expect_ident_like()?;
1276 if !if_kw.eq_ignore_ascii_case("if") {
1277 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
1278 }
1279 Ok(PlPgSqlStmt::If {
1280 branches,
1281 else_branch,
1282 })
1283 }
1284
1285 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1289 let lvl_ident = self.expect_ident_like()?;
1290 let level = match lvl_ident.to_ascii_lowercase().as_str() {
1291 "notice" => RaiseLevel::Notice,
1292 "warning" => RaiseLevel::Warning,
1293 "info" => RaiseLevel::Info,
1294 "log" => RaiseLevel::Log,
1295 "debug" => RaiseLevel::Debug,
1296 "exception" => RaiseLevel::Exception,
1297 other => {
1298 return Err(self.err(alloc::format!(
1299 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
1300 )));
1301 }
1302 };
1303 let Token::String(msg) = self.peek() else {
1307 return Err(self.err(alloc::format!(
1308 "expected RAISE message string, got {:?}",
1309 self.peek()
1310 )));
1311 };
1312 let message = msg.clone();
1313 self.advance();
1314 let mut args: Vec<Expr> = Vec::new();
1316 while matches!(self.peek(), Token::Comma) {
1317 self.advance();
1318 args.push(self.parse_expr(0)?);
1319 }
1320 Ok(PlPgSqlStmt::Raise {
1321 level,
1322 message,
1323 args,
1324 })
1325 }
1326
1327 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
1328 let head = self.expect_ident_like()?;
1329 if matches!(self.peek(), Token::Dot) {
1330 self.advance();
1331 let col = self.expect_ident_like()?;
1332 if head.eq_ignore_ascii_case("new") {
1333 return Ok(AssignTarget::NewColumn(col));
1334 }
1335 if head.eq_ignore_ascii_case("old") {
1336 return Ok(AssignTarget::OldColumn(col));
1337 }
1338 return Err(self.err(alloc::format!(
1339 "v7.12.4 plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
1340 got {head:?}.<col>"
1341 )));
1342 }
1343 Ok(AssignTarget::Local(head))
1344 }
1345
1346 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
1347 match self.peek() {
1349 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
1350 self.advance();
1351 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
1352 }
1353 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
1354 self.advance();
1355 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
1356 }
1357 Token::Null => {
1358 self.advance();
1359 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1360 }
1361 Token::Semicolon => {
1364 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
1365 }
1366 _ => {}
1367 }
1368 let e = self.parse_expr(0)?;
1370 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
1371 }
1372
1373 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
1374 if matches!(self.peek(), Token::Insert) {
1379 self.advance();
1380 return Ok(TriggerEvent::Insert);
1381 }
1382 match self.peek() {
1383 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
1384 self.advance();
1385 Ok(TriggerEvent::Update)
1386 }
1387 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
1388 self.advance();
1389 Ok(TriggerEvent::Delete)
1390 }
1391 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
1392 self.advance();
1393 Ok(TriggerEvent::Truncate)
1394 }
1395 other => Err(self.err(alloc::format!(
1396 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
1397 ))),
1398 }
1399 }
1400
1401 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
1408 let name = self.expect_ident_or_string()?;
1409 let scope = if matches!(self.peek(), Token::For) {
1412 self.advance();
1413 if matches!(self.peek(), Token::All) {
1414 self.advance();
1415 if !matches!(self.peek(), Token::Tables) {
1416 return Err(self.err(format!(
1417 "expected TABLES after FOR ALL, got {:?}",
1418 self.peek()
1419 )));
1420 }
1421 self.advance();
1422 if matches!(self.peek(), Token::Except) {
1423 self.advance();
1424 let tables = self.parse_publication_table_list()?;
1425 PublicationScope::AllTablesExcept(tables)
1426 } else {
1427 PublicationScope::AllTables
1428 }
1429 } else if matches!(self.peek(), Token::Table | Token::Tables) {
1430 self.advance();
1433 let tables = self.parse_publication_table_list()?;
1434 PublicationScope::ForTables(tables)
1435 } else {
1436 return Err(self.err(format!(
1437 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
1438 self.peek()
1439 )));
1440 }
1441 } else {
1442 PublicationScope::AllTables
1443 };
1444 Ok(Statement::CreatePublication(CreatePublicationStatement {
1445 name,
1446 scope,
1447 }))
1448 }
1449
1450 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
1455 let first = self.expect_ident_like()?;
1456 let mut out = alloc::vec![first];
1457 while matches!(self.peek(), Token::Comma) {
1458 self.advance();
1459 out.push(self.expect_ident_like()?);
1460 }
1461 Ok(out)
1462 }
1463
1464 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
1472 let name = self.expect_ident_or_string()?;
1473 if !matches!(self.peek(), Token::Connection) {
1474 return Err(self.err(format!(
1475 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
1476 self.peek()
1477 )));
1478 }
1479 self.advance();
1480 let conn_str = self.expect_string_literal()?;
1481 if !matches!(self.peek(), Token::Publication) {
1482 return Err(self.err(format!(
1483 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
1484 self.peek()
1485 )));
1486 }
1487 self.advance();
1488 let first = self.expect_ident_like()?;
1491 let mut publications = alloc::vec![first];
1492 while matches!(self.peek(), Token::Comma) {
1493 self.advance();
1494 publications.push(self.expect_ident_like()?);
1495 }
1496 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
1497 name,
1498 conn_str,
1499 publications,
1500 }))
1501 }
1502
1503 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
1510 let mut name = self.expect_ident_like()?;
1511 while matches!(self.peek(), Token::Dot) {
1512 self.advance();
1513 let next = self.expect_ident_like()?;
1514 name.push('.');
1515 name.push_str(&next);
1516 }
1517 Ok(name.to_ascii_lowercase())
1518 }
1519
1520 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
1521 match self.advance() {
1522 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
1523 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
1524 Ok(crate::ast::SetValue::Default)
1525 }
1526 Token::Ident(s) | Token::QuotedIdent(s) => {
1527 let mut accum = s;
1528 while matches!(self.peek(), Token::Dot) {
1529 self.advance();
1530 let next = self.expect_ident_like()?;
1531 accum.push('.');
1532 accum.push_str(&next);
1533 }
1534 Ok(crate::ast::SetValue::Ident(accum))
1535 }
1536 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
1537 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
1538 other => Err(self.err(format!(
1539 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
1540 ))),
1541 }
1542 }
1543
1544 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
1545 if !matches!(self.peek(), Token::For) {
1549 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
1550 }
1551 self.advance();
1552 self.expect_keyword_ident("wal")?;
1553 self.expect_keyword_ident("position")?;
1554 let pos = self.expect_u64_literal()?;
1555 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
1556 {
1557 self.advance();
1558 self.expect_keyword_ident("timeout")?;
1559 Some(self.expect_u64_literal()?)
1560 } else {
1561 None
1562 };
1563 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
1564 }
1565
1566 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
1570 match self.advance() {
1571 Token::Integer(n) if n >= 0 => Ok(n as u64),
1572 Token::Integer(n) => Err(ParseError {
1573 message: format!("expected non-negative integer, got {n}"),
1574 token_pos: self.pos.saturating_sub(1),
1575 }),
1576 other => Err(ParseError {
1577 message: format!("expected integer literal, got {other:?}"),
1578 token_pos: self.pos.saturating_sub(1),
1579 }),
1580 }
1581 }
1582
1583 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
1587 let name = self.expect_ident_or_string()?;
1588 self.expect_keyword_ident("with")?;
1589 self.expect_keyword_ident("password")?;
1590 let password = self.expect_string_literal()?;
1591 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
1592 && s.eq_ignore_ascii_case("role")
1593 {
1594 self.advance();
1595 self.expect_string_literal()?
1596 } else {
1597 "readonly".to_string()
1598 };
1599 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
1600 name,
1601 password,
1602 role,
1603 }))
1604 }
1605
1606 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
1609 let table = self.expect_ident_like()?;
1610 self.expect_keyword_ident("set")?;
1611 let mut assignments = Vec::new();
1612 loop {
1613 let col = self.expect_ident_like()?;
1614 if !matches!(self.peek(), Token::Eq) {
1615 return Err(self.err(format!(
1616 "expected `=` after column name in UPDATE SET, got {:?}",
1617 self.peek()
1618 )));
1619 }
1620 self.advance();
1621 let value = self.parse_expr(0)?;
1622 assignments.push((col, value));
1623 if matches!(self.peek(), Token::Comma) {
1624 self.advance();
1625 continue;
1626 }
1627 break;
1628 }
1629 let where_ = if matches!(self.peek(), Token::Where) {
1630 self.advance();
1631 Some(self.parse_expr(0)?)
1632 } else {
1633 None
1634 };
1635 let returning = self.parse_optional_returning()?;
1636 Ok(Statement::Update(crate::ast::UpdateStatement {
1637 table,
1638 assignments,
1639 where_,
1640 returning,
1641 }))
1642 }
1643
1644 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
1647 if !matches!(self.peek(), Token::From) {
1648 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
1649 }
1650 self.advance();
1651 let table = self.expect_ident_like()?;
1652 let where_ = if matches!(self.peek(), Token::Where) {
1653 self.advance();
1654 Some(self.parse_expr(0)?)
1655 } else {
1656 None
1657 };
1658 let returning = self.parse_optional_returning()?;
1659 Ok(Statement::Delete(crate::ast::DeleteStatement {
1660 table,
1661 where_,
1662 returning,
1663 }))
1664 }
1665
1666 fn parse_optional_returning(
1671 &mut self,
1672 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
1673 let is_returning_kw = matches!(
1674 self.peek(),
1675 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
1676 );
1677 if !is_returning_kw {
1678 return Ok(None);
1679 }
1680 self.advance();
1681 let mut items = Vec::new();
1682 loop {
1683 items.push(self.parse_select_item()?);
1684 if matches!(self.peek(), Token::Comma) {
1685 self.advance();
1686 continue;
1687 }
1688 break;
1689 }
1690 Ok(Some(items))
1691 }
1692
1693 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
1701 match self.advance() {
1703 Token::Index => {}
1704 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
1705 Token::Table => return self.parse_alter_table_after_keyword(),
1707 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
1708 return self.parse_alter_table_after_keyword();
1709 }
1710 other => {
1711 return Err(self.err(format!(
1712 "expected INDEX or TABLE after ALTER, got {other:?}"
1713 )));
1714 }
1715 }
1716 let name = self.expect_ident_like()?;
1717 self.expect_keyword_ident("rebuild")?;
1719 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
1721 self.advance();
1722 if !matches!(self.peek(), Token::LParen) {
1723 return Err(self.err(format!(
1724 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
1725 self.peek()
1726 )));
1727 }
1728 self.advance();
1729 self.expect_keyword_ident("encoding")?;
1730 if !matches!(self.peek(), Token::Eq) {
1731 return Err(self.err(format!(
1732 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
1733 self.peek()
1734 )));
1735 }
1736 self.advance();
1737 let enc_ident = match self.advance() {
1738 Token::Ident(s) | Token::QuotedIdent(s) => s,
1739 other => {
1740 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
1741 }
1742 };
1743 let enc = match enc_ident.to_ascii_lowercase().as_str() {
1744 "f32" => VecEncoding::F32,
1745 "sq8" => VecEncoding::Sq8,
1746 "half" => VecEncoding::F16,
1747 other => {
1748 return Err(self.err(format!(
1749 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
1750 )));
1751 }
1752 };
1753 if !matches!(self.peek(), Token::RParen) {
1754 return Err(self.err(format!(
1755 "expected ')' after encoding value, got {:?}",
1756 self.peek()
1757 )));
1758 }
1759 self.advance();
1760 Some(enc)
1761 } else {
1762 None
1763 };
1764 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
1765 name,
1766 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
1767 }))
1768 }
1769
1770 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
1774 let table_name = self.expect_ident_like()?;
1775 match self.peek() {
1779 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
1780 self.advance();
1781 let setting = self.expect_ident_like()?;
1782 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
1783 return Err(self.err(alloc::format!(
1784 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
1785 )));
1786 }
1787 if !matches!(self.peek(), Token::Eq) {
1788 return Err(self.err(alloc::format!(
1789 "expected '=' after hot_tier_bytes, got {:?}",
1790 self.peek()
1791 )));
1792 }
1793 self.advance();
1794 let n = self.expect_u64_literal()?;
1795 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1796 name: table_name,
1797 target: crate::ast::AlterTableTarget::SetHotTierBytes(n),
1798 }))
1799 }
1800 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
1801 self.advance();
1802 let is_fk = matches!(
1808 self.peek(),
1809 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
1810 || s.eq_ignore_ascii_case("foreign")
1811 );
1812 if is_fk {
1813 let fk = self.parse_table_level_fk()?;
1814 return Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1815 name: table_name,
1816 target: crate::ast::AlterTableTarget::AddForeignKey(fk),
1817 }));
1818 }
1819 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
1822 self.advance();
1823 }
1824 let mut if_not_exists = false;
1827 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
1828 self.advance();
1829 if !matches!(self.peek(), Token::Not) {
1830 return Err(self.err(alloc::format!(
1831 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
1832 self.peek()
1833 )));
1834 }
1835 self.advance();
1836 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
1837 return Err(self.err(alloc::format!(
1838 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
1839 self.peek()
1840 )));
1841 }
1842 self.advance();
1843 if_not_exists = true;
1844 }
1845 let column = self.parse_column_def()?;
1846 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1847 name: table_name,
1848 target: crate::ast::AlterTableTarget::AddColumn {
1849 column,
1850 if_not_exists,
1851 },
1852 }))
1853 }
1854 Token::Drop => {
1855 self.advance();
1856 match self.advance() {
1857 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
1858 other => {
1859 return Err(self.err(alloc::format!(
1860 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
1861 )));
1862 }
1863 }
1864 let cname = self.expect_ident_like()?;
1865 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1866 name: table_name,
1867 target: crate::ast::AlterTableTarget::DropForeignKey(cname),
1868 }))
1869 }
1870 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
1873 self.advance();
1874 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
1876 self.advance();
1877 }
1878 let col_name = self.expect_ident_like()?;
1879 match self.peek() {
1881 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
1882 self.advance();
1883 }
1884 other => {
1885 return Err(self.err(alloc::format!(
1886 "expected TYPE after ALTER COLUMN <name>, got {other:?}"
1887 )));
1888 }
1889 }
1890 let new_type = self.parse_column_type_name()?;
1891 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
1892 {
1893 self.advance();
1894 Some(self.parse_expr(0)?)
1895 } else {
1896 None
1897 };
1898 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1899 name: table_name,
1900 target: crate::ast::AlterTableTarget::AlterColumnType {
1901 column: col_name,
1902 new_type,
1903 using,
1904 },
1905 }))
1906 }
1907 other => Err(self.err(alloc::format!(
1908 "expected SET / ADD / DROP / ALTER in ALTER TABLE, got {other:?}"
1909 ))),
1910 }
1911 }
1912
1913 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
1915 match self.advance() {
1916 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
1917 other => Err(ParseError {
1918 message: format!("expected {kw:?}, got {other:?}"),
1919 token_pos: self.pos.saturating_sub(1),
1920 }),
1921 }
1922 }
1923
1924 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
1928 match self.advance() {
1929 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
1930 other => Err(ParseError {
1931 message: format!("expected identifier or string, got {other:?}"),
1932 token_pos: self.pos.saturating_sub(1),
1933 }),
1934 }
1935 }
1936
1937 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
1938 match self.advance() {
1939 Token::String(s) => Ok(s),
1940 other => Err(ParseError {
1941 message: format!("expected quoted string, got {other:?}"),
1942 token_pos: self.pos.saturating_sub(1),
1943 }),
1944 }
1945 }
1946
1947 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
1948 let mut head = self.parse_bare_select()?;
1953 while matches!(self.peek(), Token::Union) {
1954 self.advance();
1955 let kind = if matches!(self.peek(), Token::All) {
1956 self.advance();
1957 UnionKind::All
1958 } else {
1959 UnionKind::Distinct
1960 };
1961 let peer = self.parse_bare_select()?;
1962 head.unions.push((kind, peer));
1963 }
1964 head.order_by = if matches!(self.peek(), Token::Order) {
1965 self.advance();
1966 if !matches!(self.peek(), Token::By) {
1967 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
1968 }
1969 self.advance();
1970 let mut keys = Vec::new();
1973 loop {
1974 let expr = self.parse_expr(0)?;
1975 let desc = if matches!(self.peek(), Token::Desc) {
1976 self.advance();
1977 true
1978 } else if matches!(self.peek(), Token::Asc) {
1979 self.advance();
1980 false
1981 } else {
1982 false
1983 };
1984 keys.push(OrderBy { expr, desc });
1985 if matches!(self.peek(), Token::Comma) {
1986 self.advance();
1987 } else {
1988 break;
1989 }
1990 }
1991 keys
1992 } else {
1993 Vec::new()
1994 };
1995 head.limit = if matches!(self.peek(), Token::Limit) {
1996 self.advance();
1997 Some(self.parse_limit_expr("LIMIT")?)
1998 } else {
1999 None
2000 };
2001 head.offset = if matches!(self.peek(), Token::Offset) {
2002 self.advance();
2003 Some(self.parse_limit_expr("OFFSET")?)
2004 } else {
2005 None
2006 };
2007 Ok(Statement::Select(head))
2008 }
2009
2010 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2015 match self.advance() {
2016 Token::Integer(n) if n >= 0 => u32::try_from(n)
2017 .map(crate::ast::LimitExpr::Literal)
2018 .map_err(|_| ParseError {
2019 message: alloc::format!("{label} value too large: {n}"),
2020 token_pos: self.pos.saturating_sub(1),
2021 }),
2022 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2023 other => Err(ParseError {
2024 message: alloc::format!(
2025 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2026 ),
2027 token_pos: self.pos.saturating_sub(1),
2028 }),
2029 }
2030 }
2031
2032 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2037 if !matches!(self.peek(), Token::Select) {
2038 return Err(self.err(format!(
2039 "expected SELECT to start a query block, got {:?}",
2040 self.peek()
2041 )));
2042 }
2043 self.advance();
2044 let distinct = if matches!(self.peek(), Token::Distinct) {
2045 self.advance();
2046 true
2047 } else {
2048 false
2049 };
2050 let items = self.parse_select_list()?;
2051 let from = if matches!(self.peek(), Token::From) {
2052 self.advance();
2053 Some(self.parse_from_clause()?)
2054 } else {
2055 None
2056 };
2057 let where_ = if matches!(self.peek(), Token::Where) {
2058 self.advance();
2059 Some(self.parse_expr(0)?)
2060 } else {
2061 None
2062 };
2063 let mut group_by_all = false;
2064 let group_by = if matches!(self.peek(), Token::Group) {
2065 self.advance();
2066 if !matches!(self.peek(), Token::By) {
2067 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2068 }
2069 self.advance();
2070 if matches!(self.peek(), Token::All) {
2073 self.advance();
2074 group_by_all = true;
2075 None
2076 } else {
2077 let mut groups = Vec::new();
2078 loop {
2079 groups.push(self.parse_expr(0)?);
2080 if matches!(self.peek(), Token::Comma) {
2081 self.advance();
2082 } else {
2083 break;
2084 }
2085 }
2086 Some(groups)
2087 }
2088 } else {
2089 None
2090 };
2091 let having = if matches!(self.peek(), Token::Having) {
2092 self.advance();
2093 Some(self.parse_expr(0)?)
2094 } else {
2095 None
2096 };
2097 Ok(SelectStatement {
2098 ctes: Vec::new(),
2099 distinct,
2100 items,
2101 from,
2102 where_,
2103 group_by,
2104 group_by_all,
2105 having,
2106 unions: Vec::new(),
2107 order_by: Vec::new(),
2108 limit: None,
2109 offset: None,
2110 })
2111 }
2112
2113 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2114 debug_assert!(matches!(self.peek(), Token::Table));
2116 self.advance();
2117 let if_not_exists = self.consume_if_not_exists();
2118 let name = self.expect_ident_like()?;
2119 if !matches!(self.peek(), Token::LParen) {
2120 return Err(self.err(format!(
2121 "expected '(' after table name, got {:?}",
2122 self.peek()
2123 )));
2124 }
2125 self.advance();
2126 let mut columns = Vec::new();
2127 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
2128 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
2129 loop {
2130 if self.peek_table_level_pk_start() {
2136 table_constraints.push(self.parse_table_level_primary_key()?);
2137 } else if self.peek_table_level_unique_start() {
2138 table_constraints.push(self.parse_table_level_unique()?);
2139 } else if self.peek_table_level_check_start() {
2140 table_constraints.push(self.parse_table_level_check()?);
2142 } else if self.peek_constraint_or_fk_start() {
2143 foreign_keys.push(self.parse_table_level_fk()?);
2144 } else {
2145 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
2146 if col.is_unique {
2150 table_constraints.push(crate::ast::TableConstraint::Unique {
2151 name: None,
2152 columns: alloc::vec![col.name.clone()],
2153 nulls_not_distinct: false,
2154 });
2155 }
2156 if let Some(check_expr) = col.check.clone() {
2157 table_constraints.push(crate::ast::TableConstraint::Check {
2158 name: None,
2159 expr: check_expr,
2160 });
2161 }
2162 columns.push(col);
2163 if let Some(fk) = col_level_fk {
2164 foreign_keys.push(fk);
2165 }
2166 }
2167 match self.peek() {
2168 Token::Comma => {
2169 self.advance();
2170 }
2171 Token::RParen => {
2172 self.advance();
2173 break;
2174 }
2175 other => {
2176 return Err(
2177 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2178 );
2179 }
2180 }
2181 }
2182 if columns.is_empty() {
2183 return Err(self.err("CREATE TABLE requires at least one column".into()));
2184 }
2185 Ok(Statement::CreateTable(CreateTableStatement {
2186 name,
2187 columns,
2188 if_not_exists,
2189 foreign_keys,
2190 table_constraints,
2191 }))
2192 }
2193
2194 fn peek_table_level_pk_start(&self) -> bool {
2199 let cur = self.peek();
2200 let nxt = self.tokens.get(self.pos + 1);
2201 let nxt2 = self.tokens.get(self.pos + 2);
2202 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
2203 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
2204 let is_lparen = matches!(nxt2, Some(Token::LParen));
2205 is_primary && is_key && is_lparen
2206 }
2207
2208 fn peek_table_level_unique_start(&self) -> bool {
2212 let cur = self.peek();
2213 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
2214 if !is_unique {
2215 return false;
2216 }
2217 let n1 = self.tokens.get(self.pos + 1);
2218 if matches!(n1, Some(Token::LParen)) {
2220 return true;
2221 }
2222 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
2224 if !is_nulls {
2225 return false;
2226 }
2227 let n2 = self.tokens.get(self.pos + 2);
2228 let n3 = self.tokens.get(self.pos + 3);
2229 let n4 = self.tokens.get(self.pos + 4);
2230 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
2232 return true;
2233 }
2234 if matches!(n2, Some(Token::Not))
2236 && matches!(n3, Some(Token::Distinct))
2237 && matches!(n4, Some(Token::LParen))
2238 {
2239 return true;
2240 }
2241 false
2242 }
2243
2244 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2245 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
2248 Ok(crate::ast::TableConstraint::PrimaryKey {
2249 name: None,
2250 columns,
2251 })
2252 }
2253
2254 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2255 self.advance(); let mut nulls_not_distinct = false;
2260 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
2261 let n1 = self.tokens.get(self.pos + 1);
2262 let n2 = self.tokens.get(self.pos + 2);
2263 let is_not = matches!(n1, Some(Token::Not));
2264 let is_distinct = matches!(n2, Some(Token::Distinct));
2265 if is_not && is_distinct {
2266 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
2270 } else if matches!(n1, Some(Token::Distinct)) {
2271 self.advance(); self.advance(); }
2274 }
2275 let columns = self.parse_paren_ident_list("UNIQUE")?;
2276 Ok(crate::ast::TableConstraint::Unique {
2277 name: None,
2278 columns,
2279 nulls_not_distinct,
2280 })
2281 }
2282
2283 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2287 self.advance(); if !matches!(self.peek(), Token::LParen) {
2289 return Err(self.err(alloc::format!(
2290 "expected '(' after CHECK, got {:?}",
2291 self.peek()
2292 )));
2293 }
2294 self.advance();
2295 let expr = self.parse_expr(0)?;
2296 if !matches!(self.peek(), Token::RParen) {
2297 return Err(self.err(alloc::format!(
2298 "expected ')' to close CHECK predicate, got {:?}",
2299 self.peek()
2300 )));
2301 }
2302 self.advance();
2303 Ok(crate::ast::TableConstraint::Check { name: None, expr })
2304 }
2305
2306 fn peek_table_level_check_start(&self) -> bool {
2308 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
2309 }
2310
2311 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
2312 if !matches!(self.peek(), Token::LParen) {
2313 return Err(self.err(alloc::format!(
2314 "expected '(' after {ctx}, got {:?}",
2315 self.peek()
2316 )));
2317 }
2318 self.advance();
2319 let mut out = Vec::new();
2320 loop {
2321 out.push(self.expect_ident_like()?);
2322 match self.peek() {
2323 Token::Comma => {
2324 self.advance();
2325 }
2326 Token::RParen => {
2327 self.advance();
2328 break;
2329 }
2330 other => {
2331 return Err(self.err(alloc::format!(
2332 "expected ',' or ')' in {ctx} list, got {other:?}"
2333 )));
2334 }
2335 }
2336 }
2337 if out.is_empty() {
2338 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
2339 }
2340 Ok(out)
2341 }
2342
2343 fn peek_constraint_or_fk_start(&self) -> bool {
2348 let is_constraint_kw = matches!(
2349 self.peek(),
2350 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2351 );
2352 let is_foreign_kw = matches!(
2353 self.peek(),
2354 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
2355 );
2356 is_constraint_kw || is_foreign_kw
2357 }
2358
2359 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
2363 let mut name: Option<String> = None;
2364 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
2365 self.advance();
2366 name = Some(self.expect_ident_like()?);
2367 }
2368 match self.advance() {
2370 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
2371 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
2372 }
2373 match self.advance() {
2375 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
2376 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
2377 }
2378 if !matches!(self.peek(), Token::LParen) {
2380 return Err(self.err(format!(
2381 "expected '(' after FOREIGN KEY, got {:?}",
2382 self.peek()
2383 )));
2384 }
2385 self.advance();
2386 let mut columns = Vec::new();
2387 loop {
2388 columns.push(self.expect_ident_like()?);
2389 match self.peek() {
2390 Token::Comma => {
2391 self.advance();
2392 }
2393 Token::RParen => {
2394 self.advance();
2395 break;
2396 }
2397 other => {
2398 return Err(self.err(format!(
2399 "expected ',' or ')' in FK column list, got {other:?}"
2400 )));
2401 }
2402 }
2403 }
2404 if columns.is_empty() {
2405 return Err(self.err("FOREIGN KEY requires at least one column".into()));
2406 }
2407 let (parent_table, parent_columns, on_delete, on_update) =
2408 self.parse_references_tail(columns.len())?;
2409 Ok(ForeignKeyConstraint {
2410 name,
2411 columns,
2412 parent_table,
2413 parent_columns,
2414 on_delete,
2415 on_update,
2416 })
2417 }
2418
2419 fn parse_references_tail(
2424 &mut self,
2425 expected_arity: usize,
2426 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
2427 match self.advance() {
2428 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
2429 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
2430 }
2431 let parent_table = self.expect_ident_like()?;
2432 let mut parent_columns: Vec<String> = Vec::new();
2433 if matches!(self.peek(), Token::LParen) {
2434 self.advance();
2435 loop {
2436 parent_columns.push(self.expect_ident_like()?);
2437 match self.peek() {
2438 Token::Comma => {
2439 self.advance();
2440 }
2441 Token::RParen => {
2442 self.advance();
2443 break;
2444 }
2445 other => {
2446 return Err(self.err(format!(
2447 "expected ',' or ')' in REFERENCES column list, got {other:?}"
2448 )));
2449 }
2450 }
2451 }
2452 }
2453 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
2454 return Err(self.err(format!(
2455 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
2456 expected_arity,
2457 parent_columns.len()
2458 )));
2459 }
2460 loop {
2466 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2467 return Err(self.err(
2468 "DEFERRABLE constraints are not supported (SPG is single-writer; \
2469 constraints are always evaluated immediately at commit)"
2470 .into(),
2471 ));
2472 }
2473 if matches!(self.peek(), Token::Not) {
2474 let look = self.tokens.get(self.pos + 1);
2475 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2476 self.advance();
2479 self.advance();
2480 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
2482 {
2483 self.advance();
2484 match self.advance() {
2485 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
2486 other => {
2487 return Err(self.err(format!(
2488 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
2489 got {other:?}"
2490 )));
2491 }
2492 }
2493 }
2494 continue;
2495 }
2496 break;
2497 }
2498 break;
2499 }
2500 let mut on_delete = FkAction::Restrict;
2503 let mut on_update = FkAction::Restrict;
2504 let mut seen_on_delete = false;
2505 let mut seen_on_update = false;
2506 loop {
2507 if !matches!(self.peek(), Token::On) {
2508 break;
2509 }
2510 self.advance();
2511 let which = self.advance();
2512 let action = self.parse_fk_action()?;
2513 match which {
2514 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
2515 if seen_on_delete {
2516 return Err(self.err("ON DELETE specified twice".into()));
2517 }
2518 seen_on_delete = true;
2519 on_delete = action;
2520 }
2521 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
2522 if seen_on_update {
2523 return Err(self.err("ON UPDATE specified twice".into()));
2524 }
2525 seen_on_update = true;
2526 on_update = action;
2527 }
2528 other => {
2529 return Err(
2530 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
2531 );
2532 }
2533 }
2534 }
2535 Ok((parent_table, parent_columns, on_delete, on_update))
2536 }
2537
2538 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
2541 match self.advance() {
2542 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
2543 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
2544 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
2545 Token::Null => Ok(FkAction::SetNull),
2546 Token::Default => Ok(FkAction::SetDefault),
2547 other => Err(self.err(format!(
2548 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
2549 ))),
2550 },
2551 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
2552 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
2553 other => Err(self.err(format!(
2554 "expected ACTION after NO in FK action, got {other:?}"
2555 ))),
2556 },
2557 other => Err(self.err(format!(
2558 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
2559 ))),
2560 }
2561 }
2562
2563 fn consume_if_not_exists(&mut self) -> bool {
2566 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2570 if !looks_like_if {
2571 return false;
2572 }
2573 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
2576 return false;
2577 }
2578 if !matches!(
2579 self.tokens.get(self.pos + 2),
2580 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2581 ) {
2582 return false;
2583 }
2584 self.advance(); self.advance(); self.advance(); true
2588 }
2589
2590 fn consume_if_exists(&mut self) -> bool {
2594 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2595 if !looks_like_if {
2596 return false;
2597 }
2598 if !matches!(
2599 self.tokens.get(self.pos + 1),
2600 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2601 ) {
2602 return false;
2603 }
2604 self.advance(); self.advance(); true
2607 }
2608
2609 fn consume_optional_index_column_qualifiers(&mut self) {
2615 loop {
2616 match self.peek() {
2617 Token::Asc | Token::Desc => {
2618 self.advance();
2619 }
2620 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
2621 let look = self.tokens.get(self.pos + 1);
2622 if matches!(
2623 look,
2624 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
2625 || k.eq_ignore_ascii_case("last")
2626 ) {
2627 self.advance();
2628 self.advance();
2629 } else {
2630 break;
2631 }
2632 }
2633 _ => break,
2634 }
2635 }
2636 }
2637
2638 fn parse_create_index_stmt_after_create(
2639 &mut self,
2640 is_unique: bool,
2641 ) -> Result<Statement, ParseError> {
2642 debug_assert!(matches!(self.peek(), Token::Index));
2644 self.advance();
2645 let if_not_exists = self.consume_if_not_exists();
2646 let name = self.expect_ident_like()?;
2647 if !matches!(self.peek(), Token::On) {
2648 return Err(self.err(format!(
2649 "expected ON after CREATE INDEX <name>, got {:?}",
2650 self.peek()
2651 )));
2652 }
2653 self.advance();
2654 let table = self.expect_ident_like()?;
2655 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2660 self.advance();
2661 let m = self.expect_ident_like()?;
2662 match m.to_ascii_lowercase().as_str() {
2663 "hnsw" => IndexMethod::Hnsw,
2664 "btree" => IndexMethod::BTree,
2665 "brin" => IndexMethod::Brin,
2666 "gin" => IndexMethod::Gin,
2671 "gist" | "spgist" | "hash" => IndexMethod::BTree,
2680 "ivfflat" => IndexMethod::Hnsw,
2688 other => {
2689 return Err(self.err(alloc::format!(
2690 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
2691 )));
2692 }
2693 }
2694 } else {
2695 IndexMethod::BTree
2696 };
2697 if !matches!(self.peek(), Token::LParen) {
2698 return Err(self.err(format!(
2699 "expected '(' before indexed column, got {:?}",
2700 self.peek()
2701 )));
2702 }
2703 self.advance();
2704 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
2713 Token::Ident(s) | Token::QuotedIdent(s)
2720 if matches!(
2721 self.tokens.get(self.pos + 1),
2722 Some(Token::RParen | Token::Comma)
2723 ) =>
2724 {
2725 self.advance();
2726 (s, None)
2727 }
2728 Token::Ident(s) | Token::QuotedIdent(s)
2736 if matches!(
2737 self.tokens.get(self.pos + 1),
2738 Some(Token::Ident(op) | Token::QuotedIdent(op))
2739 if is_vector_opclass_name(op)
2740 ) =>
2741 {
2742 self.advance(); self.advance(); (s, None)
2745 }
2746 Token::Ident(_) | Token::QuotedIdent(_) => {
2747 let key_expr = self.parse_expr(0)?;
2748 let primary = extract_first_column(&key_expr).ok_or_else(|| {
2749 self.err("expression index key must reference at least one column".into())
2750 })?;
2751 (primary, Some(key_expr))
2752 }
2753 other => {
2754 return Err(self.err(format!(
2755 "expected column ident or expression, got {other:?}"
2756 )));
2757 }
2758 };
2759 let mut extra_columns: Vec<String> = Vec::new();
2768 self.consume_optional_index_column_qualifiers();
2770 while matches!(self.peek(), Token::Comma) {
2771 self.advance();
2772 let extra = self.expect_ident_like()?;
2773 self.consume_optional_index_column_qualifiers();
2774 extra_columns.push(extra);
2775 }
2776 if !matches!(self.peek(), Token::RParen) {
2777 return Err(self.err(format!(
2778 "expected ')' after indexed column / expression, got {:?}",
2779 self.peek()
2780 )));
2781 }
2782 self.advance();
2783 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
2787 {
2788 self.advance();
2789 if !matches!(self.peek(), Token::LParen) {
2790 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
2791 }
2792 self.advance();
2793 let mut cols = Vec::new();
2794 loop {
2795 cols.push(self.expect_ident_like()?);
2796 match self.peek() {
2797 Token::Comma => {
2798 self.advance();
2799 }
2800 Token::RParen => {
2801 self.advance();
2802 break;
2803 }
2804 other => {
2805 return Err(self.err(format!(
2806 "expected ',' or ')' in INCLUDE list, got {other:?}"
2807 )));
2808 }
2809 }
2810 }
2811 cols
2812 } else {
2813 Vec::new()
2814 };
2815 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2821 self.advance();
2822 if !matches!(self.peek(), Token::LParen) {
2823 return Err(self.err(format!(
2824 "expected '(' after WITH in CREATE INDEX, got {:?}",
2825 self.peek()
2826 )));
2827 }
2828 self.advance();
2829 loop {
2830 if matches!(self.peek(), Token::RParen) {
2831 self.advance();
2832 break;
2833 }
2834 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
2837 self.advance();
2838 let _ = self.advance(); }
2840 match self.peek() {
2841 Token::Comma => {
2842 self.advance();
2843 }
2844 Token::RParen => {
2845 self.advance();
2846 break;
2847 }
2848 other => {
2849 return Err(self.err(format!(
2850 "expected ',' or ')' in WITH (…) clause, got {other:?}"
2851 )));
2852 }
2853 }
2854 }
2855 }
2856 let partial_predicate = if matches!(self.peek(), Token::Where) {
2858 self.advance();
2859 Some(self.parse_expr(0)?)
2860 } else {
2861 None
2862 };
2863 if is_unique && !matches!(method, IndexMethod::BTree) {
2868 return Err(self.err(alloc::format!(
2869 "UNIQUE is only supported on BTree indexes, got USING {:?}",
2870 method
2871 )));
2872 }
2873 Ok(Statement::CreateIndex(CreateIndexStatement {
2874 name,
2875 table,
2876 column,
2877 method,
2878 if_not_exists,
2879 included_columns,
2880 partial_predicate,
2881 extra_columns: extra_columns.clone(),
2882 expression,
2883 is_unique,
2884 }))
2885 }
2886
2887 fn parse_column_def_with_fk(
2892 &mut self,
2893 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
2894 let col = self.parse_column_def()?;
2895 let inline_references = matches!(
2897 self.peek(),
2898 Token::Ident(s) if s.eq_ignore_ascii_case("references")
2899 );
2900 if !inline_references {
2901 return Ok((col, None));
2902 }
2903 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
2904 let fk = ForeignKeyConstraint {
2905 name: None,
2906 columns: vec![col.name.clone()],
2907 parent_table,
2908 parent_columns,
2909 on_delete,
2910 on_update,
2911 };
2912 Ok((col, Some(fk)))
2913 }
2914
2915 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
2923 let (ty, _, _) = self.parse_type_with_implied_flags()?;
2924 Ok(ty)
2925 }
2926
2927 fn parse_type_with_implied_flags(
2928 &mut self,
2929 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
2930 let ty_ident = match self.advance() {
2931 Token::Ident(s) => s,
2932 other => {
2933 return Err(ParseError {
2934 message: format!("expected column type, got {other:?}"),
2935 token_pos: self.pos.saturating_sub(1),
2936 });
2937 }
2938 };
2939 let mut implied_auto_increment = false;
2940 let mut implied_not_null = false;
2941 let mut ty = match ty_ident.as_str() {
2942 "smallserial" | "serial2" => {
2944 implied_auto_increment = true;
2945 implied_not_null = true;
2946 ColumnTypeName::SmallInt
2947 }
2948 "serial" | "serial4" => {
2949 implied_auto_increment = true;
2950 implied_not_null = true;
2951 ColumnTypeName::Int
2952 }
2953 "bigserial" | "serial8" => {
2954 implied_auto_increment = true;
2955 implied_not_null = true;
2956 ColumnTypeName::BigInt
2957 }
2958 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
2964 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
2966 "bigint" => ColumnTypeName::BigInt,
2967 "float" | "double" | "real" => {
2972 if ty_ident.eq_ignore_ascii_case("double")
2973 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
2974 {
2975 self.advance();
2976 }
2977 ColumnTypeName::Float
2978 }
2979 "float4" | "float8" => ColumnTypeName::Float,
2981 "text" => ColumnTypeName::Text,
2982 "bool" | "boolean" => ColumnTypeName::Bool,
2983 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
2984 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
2985 "vector" => {
2986 let dim = self.parse_paren_size("VECTOR")?;
2987 let encoding = self.parse_optional_vector_encoding()?;
2988 ColumnTypeName::Vector { dim, encoding }
2989 }
2990 "numeric" => {
2991 let (precision, scale) = self.parse_optional_numeric_params()?;
2992 ColumnTypeName::Numeric(precision, scale)
2993 }
2994 "date" => ColumnTypeName::Date,
2995 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
2998 "timestamptz" => ColumnTypeName::Timestamptz,
3002 "json" => ColumnTypeName::Json,
3007 "jsonb" => ColumnTypeName::Jsonb,
3008 "bytea" | "bytes" => ColumnTypeName::Bytes,
3014 "tsvector" => ColumnTypeName::TsVector,
3019 "tsquery" => ColumnTypeName::TsQuery,
3020 other => {
3021 return Err(ParseError {
3022 message: format!("unsupported column type {other:?}"),
3023 token_pos: self.pos.saturating_sub(1),
3024 });
3025 }
3026 };
3027 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
3032 self.advance();
3033 }
3034 if matches!(self.peek(), Token::LBracket) {
3039 self.advance();
3040 if !matches!(self.peek(), Token::RBracket) {
3041 return Err(self.err(alloc::format!(
3042 "TEXT[] takes no dimension; got {:?}",
3043 self.peek()
3044 )));
3045 }
3046 self.advance();
3047 ty = match ty {
3051 ColumnTypeName::Text => ColumnTypeName::TextArray,
3052 ColumnTypeName::Int => ColumnTypeName::IntArray,
3053 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
3054 other => {
3055 return Err(self.err(alloc::format!(
3056 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
3057 )));
3058 }
3059 };
3060 }
3061 Ok((ty, implied_auto_increment, implied_not_null))
3062 }
3063
3064 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
3065 let name = self.expect_ident_like()?;
3066 let (ty, implied_auto_increment, implied_not_null) =
3067 self.parse_type_with_implied_flags()?;
3068 let mut default: Option<Expr> = None;
3072 let mut nullable = !implied_not_null;
3073 let mut nullability_seen = implied_not_null;
3074 let mut auto_increment = implied_auto_increment;
3075 let mut is_primary_key = false;
3076 let mut is_unique = false;
3077 let mut check: Option<Expr> = None;
3078 loop {
3079 if matches!(self.peek(), Token::Default) {
3080 if default.is_some() {
3081 return Err(self.err("DEFAULT specified twice".into()));
3082 }
3083 self.advance();
3084 default = Some(self.parse_expr(0)?);
3085 continue;
3086 }
3087 if matches!(self.peek(), Token::Not) {
3088 if nullability_seen {
3089 return Err(self.err("NOT NULL specified twice".into()));
3090 }
3091 self.advance();
3092 if !matches!(self.peek(), Token::Null) {
3093 return Err(self.err(format!(
3094 "expected NULL after NOT in column def, got {:?}",
3095 self.peek()
3096 )));
3097 }
3098 self.advance();
3099 nullable = false;
3100 nullability_seen = true;
3101 continue;
3102 }
3103 if let Token::Ident(s) = self.peek()
3106 && (s.eq_ignore_ascii_case("auto_increment")
3107 || s.eq_ignore_ascii_case("autoincrement"))
3108 {
3109 if auto_increment {
3110 return Err(self.err("AUTO_INCREMENT specified twice".into()));
3111 }
3112 self.advance();
3113 auto_increment = true;
3114 continue;
3115 }
3116 if let Token::Ident(s) = self.peek()
3121 && s.eq_ignore_ascii_case("primary")
3122 {
3123 if is_primary_key {
3124 return Err(self.err("PRIMARY KEY specified twice".into()));
3125 }
3126 let next = self.tokens.get(self.pos + 1);
3128 let next_is_key = matches!(
3129 next,
3130 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
3131 );
3132 if !next_is_key {
3133 return Err(self.err(format!(
3134 "expected KEY after PRIMARY in column def, got {:?}",
3135 next
3136 )));
3137 }
3138 self.advance(); self.advance(); is_primary_key = true;
3141 if nullability_seen && nullable {
3142 return Err(self.err(
3143 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
3144 ));
3145 }
3146 nullable = false;
3147 nullability_seen = true;
3148 continue;
3149 }
3150 if let Token::Ident(s) = self.peek()
3154 && s.eq_ignore_ascii_case("unique")
3155 {
3156 if is_unique {
3157 return Err(self.err("UNIQUE specified twice".into()));
3158 }
3159 self.advance();
3160 is_unique = true;
3161 continue;
3162 }
3163 if let Token::Ident(s) = self.peek()
3168 && s.eq_ignore_ascii_case("check")
3169 {
3170 self.advance();
3171 if !matches!(self.peek(), Token::LParen) {
3172 return Err(self.err(alloc::format!(
3173 "expected '(' after CHECK in column def, got {:?}",
3174 self.peek()
3175 )));
3176 }
3177 self.advance();
3178 let pred = self.parse_expr(0)?;
3179 if !matches!(self.peek(), Token::RParen) {
3180 return Err(self.err(alloc::format!(
3181 "expected ')' to close CHECK predicate, got {:?}",
3182 self.peek()
3183 )));
3184 }
3185 self.advance();
3186 check = Some(match check.take() {
3187 Some(prev) => Expr::Binary {
3188 op: BinOp::And,
3189 lhs: Box::new(prev),
3190 rhs: Box::new(pred),
3191 },
3192 None => pred,
3193 });
3194 continue;
3195 }
3196 break;
3197 }
3198 Ok(ColumnDef {
3199 name,
3200 ty,
3201 nullable,
3202 default,
3203 auto_increment,
3204 is_primary_key,
3205 is_unique,
3206 check,
3207 })
3208 }
3209
3210 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
3214 if !matches!(self.peek(), Token::LParen) {
3215 return Ok((0, 0));
3219 }
3220 self.advance();
3221 let precision = match self.advance() {
3222 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
3223 other => {
3224 return Err(ParseError {
3225 message: format!(
3226 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
3227 ),
3228 token_pos: self.pos.saturating_sub(1),
3229 });
3230 }
3231 };
3232 let scale = if matches!(self.peek(), Token::Comma) {
3233 self.advance();
3234 match self.advance() {
3235 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
3236 u8::try_from(n).expect("range-checked")
3237 }
3238 other => {
3239 return Err(ParseError {
3240 message: format!(
3241 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
3242 ),
3243 token_pos: self.pos.saturating_sub(1),
3244 });
3245 }
3246 }
3247 } else {
3248 0
3249 };
3250 if !matches!(self.peek(), Token::RParen) {
3251 return Err(self.err(format!(
3252 "expected ')' to close NUMERIC params, got {:?}",
3253 self.peek()
3254 )));
3255 }
3256 self.advance();
3257 Ok((precision, scale))
3258 }
3259
3260 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
3268 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3269 return Ok(VecEncoding::F32);
3270 }
3271 self.advance();
3272 let enc_ident = match self.advance() {
3273 Token::Ident(s) => s,
3274 other => {
3275 return Err(self.err(format!(
3276 "expected vector encoding after USING, got {other:?}"
3277 )));
3278 }
3279 };
3280 match enc_ident.to_ascii_lowercase().as_str() {
3281 "sq8" => Ok(VecEncoding::Sq8),
3282 "half" => Ok(VecEncoding::F16),
3285 other => Err(self.err(format!(
3286 "unknown vector encoding {other:?}; supported: SQ8, HALF"
3287 ))),
3288 }
3289 }
3290
3291 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
3292 if !matches!(self.peek(), Token::LParen) {
3293 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
3294 }
3295 self.advance();
3296 let n = match self.advance() {
3297 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
3298 message: format!("{label} size too large: {n}"),
3299 token_pos: self.pos.saturating_sub(1),
3300 })?,
3301 other => {
3302 return Err(ParseError {
3303 message: format!("expected positive integer {label} size, got {other:?}"),
3304 token_pos: self.pos.saturating_sub(1),
3305 });
3306 }
3307 };
3308 if !matches!(self.peek(), Token::RParen) {
3309 return Err(self.err(format!(
3310 "expected ')' after {label} size, got {:?}",
3311 self.peek()
3312 )));
3313 }
3314 self.advance();
3315 Ok(n)
3316 }
3317
3318 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
3319 debug_assert!(matches!(self.peek(), Token::Insert));
3320 self.advance();
3321 if !matches!(self.peek(), Token::Into) {
3322 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
3323 }
3324 self.advance();
3325 let table = self.expect_ident_like()?;
3326 let columns = if matches!(self.peek(), Token::LParen) {
3328 self.advance();
3329 let mut names = Vec::new();
3330 loop {
3331 names.push(self.expect_ident_like()?);
3332 match self.peek() {
3333 Token::Comma => {
3334 self.advance();
3335 }
3336 Token::RParen => {
3337 self.advance();
3338 break;
3339 }
3340 other => {
3341 return Err(self.err(format!(
3342 "expected ',' or ')' in INSERT column list, got {other:?}"
3343 )));
3344 }
3345 }
3346 }
3347 Some(names)
3348 } else {
3349 None
3350 };
3351 if matches!(self.peek(), Token::Select) {
3354 let select_stmt = match self.parse_select_stmt()? {
3355 Statement::Select(s) => s,
3356 other => {
3357 return Err(self.err(alloc::format!(
3358 "expected SELECT after INSERT INTO ... target, got {other:?}"
3359 )));
3360 }
3361 };
3362 let on_conflict = self.parse_optional_on_conflict()?;
3363 let returning = self.parse_optional_returning()?;
3364 return Ok(Statement::Insert(InsertStatement {
3365 table,
3366 columns,
3367 rows: Vec::new(),
3368 select_source: Some(Box::new(select_stmt)),
3369 on_conflict,
3370 returning,
3371 }));
3372 }
3373 if !matches!(self.peek(), Token::Values) {
3374 return Err(self.err(format!(
3375 "expected VALUES or SELECT after table name, got {:?}",
3376 self.peek()
3377 )));
3378 }
3379 self.advance();
3380 if !matches!(self.peek(), Token::LParen) {
3381 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
3382 }
3383 let mut rows = Vec::new();
3384 loop {
3385 if !matches!(self.peek(), Token::LParen) {
3387 return Err(self.err(format!(
3388 "expected '(' for next VALUES tuple, got {:?}",
3389 self.peek()
3390 )));
3391 }
3392 self.advance();
3393 let mut tuple = Vec::new();
3394 loop {
3395 tuple.push(self.parse_expr(0)?);
3396 match self.peek() {
3397 Token::Comma => {
3398 self.advance();
3399 }
3400 Token::RParen => {
3401 self.advance();
3402 break;
3403 }
3404 other => {
3405 return Err(self.err(format!(
3406 "expected ',' or ')' in VALUES tuple, got {other:?}"
3407 )));
3408 }
3409 }
3410 }
3411 if tuple.is_empty() {
3412 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
3413 }
3414 rows.push(tuple);
3415 if matches!(self.peek(), Token::Comma) {
3417 self.advance();
3418 } else {
3419 break;
3420 }
3421 }
3422 let on_conflict = self.parse_optional_on_conflict()?;
3423 let returning = self.parse_optional_returning()?;
3424 Ok(Statement::Insert(InsertStatement {
3425 table,
3426 columns,
3427 rows,
3428 select_source: None,
3429 on_conflict,
3430 returning,
3431 }))
3432 }
3433
3434 fn parse_optional_on_conflict(
3439 &mut self,
3440 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
3441 if !matches!(self.peek(), Token::On) {
3442 return Ok(None);
3443 }
3444 let next_is_conflict = matches!(
3447 self.tokens.get(self.pos + 1),
3448 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
3449 );
3450 if !next_is_conflict {
3451 return Ok(None);
3452 }
3453 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
3457 if matches!(self.peek(), Token::LParen) {
3458 self.advance();
3459 loop {
3460 target_columns.push(self.expect_ident_like()?);
3461 match self.peek() {
3462 Token::Comma => {
3463 self.advance();
3464 }
3465 Token::RParen => {
3466 self.advance();
3467 break;
3468 }
3469 other => {
3470 return Err(self.err(alloc::format!(
3471 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
3472 )));
3473 }
3474 }
3475 }
3476 }
3477 match self.advance() {
3479 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
3480 other => {
3481 return Err(self.err(alloc::format!(
3482 "expected DO after ON CONFLICT [(…)], got {other:?}"
3483 )));
3484 }
3485 }
3486 let action = match self.advance() {
3488 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
3489 crate::ast::OnConflictAction::Nothing
3490 }
3491 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3492 self.parse_on_conflict_update_action()?
3493 }
3494 other => {
3495 return Err(self.err(alloc::format!(
3496 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
3497 )));
3498 }
3499 };
3500 Ok(Some(crate::ast::OnConflictClause {
3501 target_columns,
3502 action,
3503 }))
3504 }
3505
3506 fn parse_on_conflict_update_action(
3510 &mut self,
3511 ) -> Result<crate::ast::OnConflictAction, ParseError> {
3512 match self.advance() {
3514 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
3515 other => {
3516 return Err(self.err(alloc::format!(
3517 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
3518 )));
3519 }
3520 }
3521 let mut assignments: Vec<(String, Expr)> = Vec::new();
3522 loop {
3523 let col = self.expect_ident_like()?;
3524 if !matches!(self.peek(), Token::Eq) {
3525 return Err(self.err(alloc::format!(
3526 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
3527 self.peek()
3528 )));
3529 }
3530 self.advance();
3531 let value = self.parse_expr(0)?;
3532 assignments.push((col, value));
3533 if matches!(self.peek(), Token::Comma) {
3534 self.advance();
3535 continue;
3536 }
3537 break;
3538 }
3539 let where_ = if matches!(self.peek(), Token::Where) {
3540 self.advance();
3541 Some(self.parse_expr(0)?)
3542 } else {
3543 None
3544 };
3545 Ok(crate::ast::OnConflictAction::Update {
3546 assignments,
3547 where_,
3548 })
3549 }
3550
3551 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
3552 let mut items = Vec::new();
3553 loop {
3554 items.push(self.parse_select_item()?);
3555 if matches!(self.peek(), Token::Comma) {
3556 self.advance();
3557 } else {
3558 break;
3559 }
3560 }
3561 Ok(items)
3562 }
3563
3564 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
3565 if matches!(self.peek(), Token::Star) {
3566 self.advance();
3567 return Ok(SelectItem::Wildcard);
3568 }
3569 let expr = self.parse_expr(0)?;
3570 let alias = self.parse_optional_alias();
3571 Ok(SelectItem::Expr { expr, alias })
3572 }
3573
3574 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
3575 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
3579 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
3580 {
3581 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
3584 if !matches!(self.peek(), Token::RParen) {
3585 return Err(self.err(alloc::format!(
3586 "expected ')' after unnest() argument, got {:?}",
3587 self.peek()
3588 )));
3589 }
3590 self.advance();
3591 let alias_ident = self.parse_optional_alias();
3592 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
3593 return Ok(TableRef {
3594 name,
3595 alias: alias_ident,
3596 as_of_segment: None,
3597 unnest_expr: Some(Box::new(expr)),
3598 });
3599 }
3600 let name = self.expect_ident_like()?;
3601 let as_of_segment = if matches!(self.peek(), Token::As)
3607 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
3608 {
3609 self.advance(); self.advance(); let kw = match self.peek().clone() {
3612 Token::Ident(s) | Token::QuotedIdent(s) => s,
3613 other => {
3614 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
3615 }
3616 };
3617 if !kw.eq_ignore_ascii_case("segment") {
3618 return Err(self.err(format!(
3619 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
3620 )));
3621 }
3622 self.advance();
3623 let id = match self.advance() {
3626 Token::String(s) => s
3627 .parse::<u32>()
3628 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3629 Token::Integer(n) => u32::try_from(n)
3630 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3631 other => {
3632 return Err(self.err(format!(
3633 "expected segment id literal after AS OF SEGMENT, got {other:?}"
3634 )));
3635 }
3636 };
3637 Some(id)
3638 } else {
3639 None
3640 };
3641 let alias = self.parse_optional_alias();
3642 Ok(TableRef {
3643 name,
3644 alias,
3645 as_of_segment,
3646 unnest_expr: None,
3647 })
3648 }
3649
3650 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
3655 let primary = self.parse_table_ref()?;
3656 let mut joins = Vec::new();
3657 loop {
3658 if matches!(self.peek(), Token::Comma) {
3660 self.advance();
3661 let table = self.parse_table_ref()?;
3662 joins.push(FromJoin {
3663 kind: JoinKind::Cross,
3664 table,
3665 on: None,
3666 });
3667 continue;
3668 }
3669 let kind =
3672 match self.peek() {
3673 Token::Inner => {
3674 self.advance();
3675 if !matches!(self.peek(), Token::Join) {
3676 return Err(self
3677 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
3678 }
3679 self.advance();
3680 JoinKind::Inner
3681 }
3682 Token::Left => {
3683 self.advance();
3684 if matches!(self.peek(), Token::Outer) {
3685 self.advance();
3686 }
3687 if !matches!(self.peek(), Token::Join) {
3688 return Err(self.err(format!(
3689 "expected JOIN after LEFT [OUTER], got {:?}",
3690 self.peek()
3691 )));
3692 }
3693 self.advance();
3694 JoinKind::Left
3695 }
3696 Token::Cross => {
3697 self.advance();
3698 if !matches!(self.peek(), Token::Join) {
3699 return Err(self
3700 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
3701 }
3702 self.advance();
3703 JoinKind::Cross
3704 }
3705 Token::Join => {
3706 self.advance();
3707 JoinKind::Inner
3708 }
3709 _ => break,
3710 };
3711 let table = self.parse_table_ref()?;
3712 let on = if matches!(self.peek(), Token::On) {
3713 self.advance();
3714 Some(self.parse_expr(0)?)
3715 } else if kind == JoinKind::Cross {
3716 None
3717 } else {
3718 return Err(self.err(format!(
3719 "expected ON after {:?} JOIN, got {:?}",
3720 kind,
3721 self.peek()
3722 )));
3723 };
3724 joins.push(FromJoin { kind, table, on });
3725 }
3726 Ok(FromClause { primary, joins })
3727 }
3728
3729 fn parse_optional_alias(&mut self) -> Option<String> {
3734 if matches!(self.peek(), Token::As) {
3735 self.advance();
3736 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3741 return self.expect_ident_like().ok();
3742 }
3743 return None;
3744 }
3745 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3746 return self.expect_ident_like().ok();
3747 }
3748 None
3749 }
3750
3751 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
3753 let mut lhs = self.parse_unary()?;
3754 while let Some((op, prec)) = binop_from(self.peek()) {
3755 if prec < min_prec {
3756 break;
3757 }
3758 self.advance();
3759 let any_kind = match self.peek() {
3764 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
3765 Some(false)
3766 }
3767 Token::Ident(s) | Token::QuotedIdent(s)
3768 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
3769 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
3770 {
3771 Some(s.eq_ignore_ascii_case("any"))
3772 }
3773 _ => None,
3774 };
3775 if let Some(is_any) = any_kind {
3776 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
3779 if !matches!(self.peek(), Token::RParen) {
3780 return Err(self.err(alloc::format!(
3781 "expected ')' after ANY/ALL argument, got {:?}",
3782 self.peek()
3783 )));
3784 }
3785 self.advance();
3786 lhs = Expr::AnyAll {
3787 expr: Box::new(lhs),
3788 op,
3789 array: Box::new(arr),
3790 is_any,
3791 };
3792 continue;
3793 }
3794 let rhs = self.parse_expr(prec + 1)?;
3795 lhs = Expr::Binary {
3796 lhs: Box::new(lhs),
3797 op,
3798 rhs: Box::new(rhs),
3799 };
3800 }
3801 Ok(lhs)
3802 }
3803
3804 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
3805 match self.peek() {
3806 Token::Not => {
3807 self.advance();
3808 let e = self.parse_expr(3)?;
3811 Ok(Expr::Unary {
3812 op: UnOp::Not,
3813 expr: Box::new(e),
3814 })
3815 }
3816 Token::Minus => {
3817 self.advance();
3818 let e = self.parse_expr(8)?;
3821 Ok(Expr::Unary {
3822 op: UnOp::Neg,
3823 expr: Box::new(e),
3824 })
3825 }
3826 _ => self.parse_atom(),
3827 }
3828 }
3829
3830 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
3831 let tok_pos = self.pos;
3832 match self.advance() {
3833 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
3834 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
3835 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
3836 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
3837 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
3838 Token::Null => Ok(Expr::Literal(Literal::Null)),
3839 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
3843 Token::LParen => {
3844 if matches!(self.peek(), Token::Select) {
3848 let inner = self.parse_select_stmt()?;
3849 match self.advance() {
3850 Token::RParen => {
3851 let Statement::Select(s) = inner else {
3852 unreachable!("parse_select_stmt returns Select")
3853 };
3854 Ok(Expr::ScalarSubquery(Box::new(s)))
3855 }
3856 other => Err(ParseError {
3857 message: format!("expected ')' after scalar subquery, got {other:?}"),
3858 token_pos: self.pos.saturating_sub(1),
3859 }),
3860 }
3861 } else {
3862 let e = self.parse_expr(0)?;
3863 match self.advance() {
3864 Token::RParen => Ok(e),
3865 other => Err(ParseError {
3866 message: format!("expected ')', got {other:?}"),
3867 token_pos: self.pos.saturating_sub(1),
3868 }),
3869 }
3870 }
3871 }
3872 Token::LBracket => self.parse_vector_literal_body(),
3873 Token::Extract => self.parse_extract_atom(),
3874 Token::Interval => self.parse_interval_atom(),
3875 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
3880 self.parse_exists_atom(false)
3881 }
3882 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
3886 self.parse_case_atom()
3887 }
3888 Token::Ident(s) | Token::QuotedIdent(s)
3892 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
3893 {
3894 self.advance(); let mut items: Vec<Expr> = Vec::new();
3896 if !matches!(self.peek(), Token::RBracket) {
3897 loop {
3898 items.push(self.parse_expr(0)?);
3899 match self.peek() {
3900 Token::Comma => {
3901 self.advance();
3902 }
3903 Token::RBracket => break,
3904 other => {
3905 return Err(self.err(alloc::format!(
3906 "expected ',' or ']' in ARRAY literal, got {other:?}"
3907 )));
3908 }
3909 }
3910 }
3911 }
3912 self.advance(); Ok(Expr::Array(items))
3914 }
3915 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
3916 other => Err(ParseError {
3917 message: format!("unexpected token {other:?} in expression"),
3918 token_pos: tok_pos,
3919 }),
3920 }
3921 .and_then(|atom| self.finish_postfix_casts(atom))
3923 }
3924
3925 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
3928 loop {
3929 if matches!(self.peek(), Token::DoubleColon) {
3930 self.advance();
3931 let target = match self.advance() {
3936 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
3937 "int" | "integer" | "int4" => {
3938 if matches!(self.peek(), Token::LBracket)
3939 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3940 {
3941 self.advance();
3942 self.advance();
3943 CastTarget::IntArray
3944 } else {
3945 CastTarget::Int
3946 }
3947 }
3948 "bigint" | "int8" => {
3949 if matches!(self.peek(), Token::LBracket)
3950 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3951 {
3952 self.advance();
3953 self.advance();
3954 CastTarget::BigIntArray
3955 } else {
3956 CastTarget::BigInt
3957 }
3958 }
3959 "float" | "double" | "real" => CastTarget::Float,
3960 "text" => {
3961 if matches!(self.peek(), Token::LBracket)
3963 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
3964 {
3965 self.advance();
3966 self.advance();
3967 CastTarget::TextArray
3968 } else {
3969 CastTarget::Text
3970 }
3971 }
3972 "bool" | "boolean" => CastTarget::Bool,
3973 "vector" => CastTarget::Vector,
3974 "date" => CastTarget::Date,
3975 "timestamp" | "datetime" => CastTarget::Timestamp,
3976 "timestamptz" => CastTarget::Timestamptz,
3977 "interval" => CastTarget::Interval,
3978 "json" => CastTarget::Json,
3979 "jsonb" => CastTarget::Jsonb,
3980 "regtype" => CastTarget::RegType,
3981 "regclass" => CastTarget::RegClass,
3982 "tsvector" => CastTarget::TsVector,
3986 "tsquery" => CastTarget::TsQuery,
3987 other => {
3988 return Err(ParseError {
3989 message: format!("unsupported cast target `::{other}`"),
3990 token_pos: self.pos.saturating_sub(1),
3991 });
3992 }
3993 },
3994 Token::Interval => CastTarget::Interval,
3995 other => {
3996 return Err(ParseError {
3997 message: format!("expected type ident after `::`, got {other:?}"),
3998 token_pos: self.pos.saturating_sub(1),
3999 });
4000 }
4001 };
4002 expr = Expr::Cast {
4003 expr: Box::new(expr),
4004 target,
4005 };
4006 continue;
4007 }
4008 if matches!(self.peek(), Token::Is) {
4009 self.advance();
4010 let negated = if matches!(self.peek(), Token::Not) {
4011 self.advance();
4012 true
4013 } else {
4014 false
4015 };
4016 if matches!(self.peek(), Token::Distinct) {
4019 self.advance();
4020 if !matches!(self.peek(), Token::From) {
4021 return Err(self.err(format!(
4022 "expected FROM after IS{} DISTINCT, got {:?}",
4023 if negated { " NOT" } else { "" },
4024 self.peek()
4025 )));
4026 }
4027 self.advance();
4028 let rhs = self.parse_expr(20)?;
4032 let op = if negated {
4033 BinOp::IsNotDistinctFrom
4034 } else {
4035 BinOp::IsDistinctFrom
4036 };
4037 expr = Expr::Binary {
4038 op,
4039 lhs: Box::new(expr),
4040 rhs: Box::new(rhs),
4041 };
4042 continue;
4043 }
4044 if !matches!(self.peek(), Token::Null) {
4045 return Err(self.err(format!(
4046 "expected NULL or DISTINCT after IS{}, got {:?}",
4047 if negated { " NOT" } else { "" },
4048 self.peek()
4049 )));
4050 }
4051 self.advance();
4052 expr = Expr::IsNull {
4053 expr: Box::new(expr),
4054 negated,
4055 };
4056 continue;
4057 }
4058 let negated = if matches!(self.peek(), Token::Not) {
4062 let next = self.tokens.get(self.pos + 1);
4063 matches!(next, Some(Token::Between | Token::In | Token::Like))
4064 } else {
4065 false
4066 };
4067 if negated {
4068 self.advance();
4069 }
4070 if matches!(self.peek(), Token::Between) {
4071 expr = self.parse_between_tail(expr, negated)?;
4072 continue;
4073 }
4074 if matches!(self.peek(), Token::In) {
4075 expr = self.parse_in_tail(expr, negated)?;
4076 continue;
4077 }
4078 if matches!(self.peek(), Token::Like) {
4079 self.advance();
4080 let pattern = self.parse_expr(5)?;
4083 expr = Expr::Like {
4084 expr: Box::new(expr),
4085 pattern: Box::new(pattern),
4086 negated,
4087 };
4088 continue;
4089 }
4090 if matches!(self.peek(), Token::LBracket) {
4094 self.advance();
4095 let index = self.parse_expr(0)?;
4096 if !matches!(self.peek(), Token::RBracket) {
4097 return Err(self.err(alloc::format!(
4098 "expected ']' after array index, got {:?}",
4099 self.peek()
4100 )));
4101 }
4102 self.advance();
4103 expr = Expr::ArraySubscript {
4104 target: Box::new(expr),
4105 index: Box::new(index),
4106 };
4107 continue;
4108 }
4109 return Ok(expr);
4110 }
4111 }
4112
4113 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4117 self.advance(); let low = self.parse_expr(5)?;
4119 if !matches!(self.peek(), Token::And) {
4120 return Err(self.err(format!(
4121 "expected AND after BETWEEN low bound, got {:?}",
4122 self.peek()
4123 )));
4124 }
4125 self.advance();
4126 let high = self.parse_expr(5)?;
4127 let target = Box::new(expr);
4128 let combined = Expr::Binary {
4129 lhs: Box::new(Expr::Binary {
4130 lhs: target.clone(),
4131 op: BinOp::GtEq,
4132 rhs: Box::new(low),
4133 }),
4134 op: BinOp::And,
4135 rhs: Box::new(Expr::Binary {
4136 lhs: target,
4137 op: BinOp::LtEq,
4138 rhs: Box::new(high),
4139 }),
4140 };
4141 Ok(maybe_not(combined, negated))
4142 }
4143
4144 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
4149 let mut recursive = false;
4154 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4155 && s.eq_ignore_ascii_case("recursive")
4156 {
4157 self.advance();
4158 recursive = true;
4159 }
4160 let mut ctes = Vec::new();
4161 loop {
4162 let name = self.expect_ident_like()?;
4163 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
4167 self.advance();
4168 let mut names = Vec::new();
4169 loop {
4170 names.push(self.expect_ident_like()?);
4171 if matches!(self.peek(), Token::Comma) {
4172 self.advance();
4173 continue;
4174 }
4175 break;
4176 }
4177 if !matches!(self.peek(), Token::RParen) {
4178 return Err(self.err(format!(
4179 "expected ')' to close CTE column list, got {:?}",
4180 self.peek()
4181 )));
4182 }
4183 self.advance();
4184 names
4185 } else {
4186 Vec::new()
4187 };
4188 if !matches!(self.peek(), Token::As) {
4192 return Err(self.err(format!(
4193 "expected AS after CTE name {name:?}, got {:?}",
4194 self.peek()
4195 )));
4196 }
4197 self.advance();
4198 if !matches!(self.peek(), Token::LParen) {
4199 return Err(self.err(format!(
4200 "expected '(' after AS in WITH clause, got {:?}",
4201 self.peek()
4202 )));
4203 }
4204 self.advance();
4205 if !matches!(self.peek(), Token::Select) {
4206 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
4207 }
4208 let inner = self.parse_select_stmt()?;
4209 if !matches!(self.peek(), Token::RParen) {
4210 return Err(self.err(format!(
4211 "expected ')' after CTE body, got {:?}",
4212 self.peek()
4213 )));
4214 }
4215 self.advance();
4216 let Statement::Select(body) = inner else {
4217 unreachable!("parse_select_stmt returns Select")
4218 };
4219 ctes.push(crate::ast::Cte {
4220 name,
4221 body,
4222 recursive,
4223 column_overrides,
4224 });
4225 if matches!(self.peek(), Token::Comma) {
4226 self.advance();
4227 continue;
4228 }
4229 break;
4230 }
4231 if !matches!(self.peek(), Token::Select) {
4233 return Err(self.err(format!(
4234 "expected SELECT after WITH clause, got {:?}",
4235 self.peek()
4236 )));
4237 }
4238 let body_stmt = self.parse_select_stmt()?;
4239 let Statement::Select(mut body) = body_stmt else {
4240 unreachable!()
4241 };
4242 body.ctes = ctes;
4243 Ok(Statement::Select(body))
4244 }
4245
4246 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
4255 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
4259 None
4260 } else {
4261 Some(Box::new(self.parse_expr(0)?))
4262 };
4263 let mut branches: Vec<(Expr, Expr)> = Vec::new();
4264 loop {
4265 match self.peek() {
4266 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
4267 self.advance();
4268 let cond = self.parse_expr(0)?;
4269 match self.peek() {
4270 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
4271 self.advance();
4272 }
4273 other => {
4274 return Err(self.err(alloc::format!(
4275 "expected THEN after CASE WHEN <expr>, got {other:?}"
4276 )));
4277 }
4278 }
4279 let value = self.parse_expr(0)?;
4280 branches.push((cond, value));
4281 }
4282 _ => break,
4283 }
4284 }
4285 if branches.is_empty() {
4286 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
4287 }
4288 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
4289 {
4290 self.advance();
4291 Some(Box::new(self.parse_expr(0)?))
4292 } else {
4293 None
4294 };
4295 match self.peek() {
4296 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
4297 self.advance();
4298 }
4299 other => {
4300 return Err(self.err(alloc::format!(
4301 "expected END to close CASE expression, got {other:?}"
4302 )));
4303 }
4304 }
4305 Ok(Expr::Case {
4306 operand,
4307 branches,
4308 else_branch,
4309 })
4310 }
4311
4312 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
4313 if !matches!(self.peek(), Token::LParen) {
4314 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
4315 }
4316 self.advance();
4317 let inner = self.parse_select_stmt()?;
4318 if !matches!(self.peek(), Token::RParen) {
4319 return Err(self.err(format!(
4320 "expected ')' after EXISTS-subquery, got {:?}",
4321 self.peek()
4322 )));
4323 }
4324 self.advance();
4325 let Statement::Select(s) = inner else {
4326 unreachable!("parse_select_stmt returns Select")
4327 };
4328 Ok(Expr::Exists {
4329 subquery: Box::new(s),
4330 negated,
4331 })
4332 }
4333
4334 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4335 self.advance(); if !matches!(self.peek(), Token::LParen) {
4337 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
4338 }
4339 self.advance();
4340 if matches!(self.peek(), Token::Select) {
4342 let inner = self.parse_select_stmt()?;
4343 if !matches!(self.peek(), Token::RParen) {
4344 return Err(self.err(format!(
4345 "expected ')' after IN-subquery, got {:?}",
4346 self.peek()
4347 )));
4348 }
4349 self.advance();
4350 let Statement::Select(s) = inner else {
4351 unreachable!("parse_select_stmt always returns Statement::Select")
4352 };
4353 return Ok(Expr::InSubquery {
4354 expr: Box::new(expr),
4355 subquery: Box::new(s),
4356 negated,
4357 });
4358 }
4359 let mut elements = Vec::new();
4360 if !matches!(self.peek(), Token::RParen) {
4361 loop {
4362 elements.push(self.parse_expr(0)?);
4363 match self.peek() {
4364 Token::Comma => {
4365 self.advance();
4366 }
4367 Token::RParen => break,
4368 other => {
4369 return Err(
4370 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
4371 );
4372 }
4373 }
4374 }
4375 }
4376 self.advance(); let target = Box::new(expr);
4378 let combined = if elements.is_empty() {
4379 Expr::Literal(Literal::Bool(false))
4380 } else {
4381 let mut iter = elements.into_iter();
4382 let first = iter.next().unwrap();
4383 let mut acc = Expr::Binary {
4384 lhs: target.clone(),
4385 op: BinOp::Eq,
4386 rhs: Box::new(first),
4387 };
4388 for elt in iter {
4389 acc = Expr::Binary {
4390 lhs: Box::new(acc),
4391 op: BinOp::Or,
4392 rhs: Box::new(Expr::Binary {
4393 lhs: target.clone(),
4394 op: BinOp::Eq,
4395 rhs: Box::new(elt),
4396 }),
4397 };
4398 }
4399 acc
4400 };
4401 Ok(maybe_not(combined, negated))
4402 }
4403
4404 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
4412 if !matches!(self.peek(), Token::LParen) {
4413 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
4414 }
4415 self.advance();
4416 let field_name = self.expect_ident_like()?;
4417 let field = match field_name.to_ascii_lowercase().as_str() {
4418 "year" => ExtractField::Year,
4419 "month" => ExtractField::Month,
4420 "day" => ExtractField::Day,
4421 "hour" => ExtractField::Hour,
4422 "minute" => ExtractField::Minute,
4423 "second" => ExtractField::Second,
4424 "microsecond" | "microseconds" => ExtractField::Microsecond,
4425 other => {
4426 return Err(self.err(format!(
4427 "unknown EXTRACT field {other:?}; \
4428 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
4429 )));
4430 }
4431 };
4432 if !matches!(self.peek(), Token::From) {
4433 return Err(self.err(format!(
4434 "expected FROM after EXTRACT field, got {:?}",
4435 self.peek()
4436 )));
4437 }
4438 self.advance();
4439 let source = self.parse_expr(0)?;
4440 if !matches!(self.peek(), Token::RParen) {
4441 return Err(self.err(format!(
4442 "expected ')' to close EXTRACT, got {:?}",
4443 self.peek()
4444 )));
4445 }
4446 self.advance();
4447 Ok(Expr::Extract {
4448 field,
4449 source: Box::new(source),
4450 })
4451 }
4452
4453 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
4458 let tok = self.advance();
4459 let Token::String(text) = tok else {
4460 return Err(self.err(format!(
4461 "expected string literal after INTERVAL, got {tok:?}"
4462 )));
4463 };
4464 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
4465 message: format!(
4466 "cannot parse INTERVAL {text:?}; \
4467 expected `<n> <unit> [<n> <unit> ...]` with units \
4468 microsecond[s], millisecond[s], second[s], minute[s], \
4469 hour[s], day[s], week[s], month[s], year[s]"
4470 ),
4471 token_pos: self.pos.saturating_sub(1),
4472 })?;
4473 Ok(Expr::Literal(Literal::Interval {
4474 months,
4475 micros,
4476 text,
4477 }))
4478 }
4479
4480 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
4481 let mut elems = Vec::new();
4482 if matches!(self.peek(), Token::RBracket) {
4483 self.advance();
4484 return Ok(Expr::Literal(Literal::Vector(elems)));
4485 }
4486 loop {
4487 let e = self.parse_expr(0)?;
4488 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
4489 message: format!("vector element must be a numeric literal, got {e:?}"),
4490 token_pos: self.pos,
4491 })?;
4492 elems.push(x);
4493 match self.peek() {
4494 Token::Comma => {
4495 self.advance();
4496 }
4497 Token::RBracket => {
4498 self.advance();
4499 break;
4500 }
4501 other => {
4502 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
4503 }
4504 }
4505 }
4506 Ok(Expr::Literal(Literal::Vector(elems)))
4507 }
4508
4509 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
4518 let Token::Ident(s) = self.peek().clone() else {
4519 return NullTreatment::Respect;
4520 };
4521 let is_ignore = s.eq_ignore_ascii_case("ignore");
4522 let is_respect = s.eq_ignore_ascii_case("respect");
4523 if !is_ignore && !is_respect {
4524 return NullTreatment::Respect;
4525 }
4526 if self.pos + 1 < self.tokens.len()
4529 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
4530 && s2.eq_ignore_ascii_case("nulls")
4531 {
4532 self.advance();
4533 self.advance();
4534 return if is_ignore {
4535 NullTreatment::Ignore
4536 } else {
4537 NullTreatment::Respect
4538 };
4539 }
4540 NullTreatment::Respect
4541 }
4542
4543 #[allow(clippy::type_complexity)] fn parse_over_clause(
4546 &mut self,
4547 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
4548 if !matches!(self.peek(), Token::LParen) {
4549 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
4550 }
4551 self.advance();
4552 let mut partition_by = Vec::new();
4553 let mut order_by = Vec::new();
4554 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4556 && s.eq_ignore_ascii_case("partition")
4557 {
4558 self.advance();
4559 if !matches!(self.peek(), Token::By) {
4560 return Err(self.err(format!(
4561 "expected BY after PARTITION, got {:?}",
4562 self.peek()
4563 )));
4564 }
4565 self.advance();
4566 loop {
4567 partition_by.push(self.parse_expr(0)?);
4568 if matches!(self.peek(), Token::Comma) {
4569 self.advance();
4570 continue;
4571 }
4572 break;
4573 }
4574 }
4575 if matches!(self.peek(), Token::Order) {
4577 self.advance();
4578 if !matches!(self.peek(), Token::By) {
4579 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4580 }
4581 self.advance();
4582 loop {
4583 let e = self.parse_expr(0)?;
4584 let desc = if matches!(self.peek(), Token::Desc) {
4585 self.advance();
4586 true
4587 } else if matches!(self.peek(), Token::Asc) {
4588 self.advance();
4589 false
4590 } else {
4591 false
4592 };
4593 order_by.push((e, desc));
4594 if matches!(self.peek(), Token::Comma) {
4595 self.advance();
4596 continue;
4597 }
4598 break;
4599 }
4600 }
4601 let mut frame: Option<WindowFrame> = None;
4605 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
4606 let kind = if s.eq_ignore_ascii_case("rows") {
4607 Some(FrameKind::Rows)
4608 } else if s.eq_ignore_ascii_case("range") {
4609 Some(FrameKind::Range)
4610 } else {
4611 None
4612 };
4613 if let Some(kind) = kind {
4614 self.advance();
4615 frame = Some(self.parse_frame_tail(kind)?);
4616 }
4617 }
4618 if !matches!(self.peek(), Token::RParen) {
4619 return Err(self.err(format!(
4620 "expected ')' to close OVER clause, got {:?}",
4621 self.peek()
4622 )));
4623 }
4624 self.advance();
4625 Ok((partition_by, order_by, frame))
4626 }
4627
4628 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
4634 if matches!(self.peek(), Token::Between) {
4635 self.advance();
4636 let start = self.parse_frame_bound()?;
4637 if !matches!(self.peek(), Token::And) {
4638 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
4639 }
4640 self.advance();
4641 let end = self.parse_frame_bound()?;
4642 Ok(WindowFrame {
4643 kind,
4644 start,
4645 end: Some(end),
4646 })
4647 } else {
4648 let start = self.parse_frame_bound()?;
4649 Ok(WindowFrame {
4650 kind,
4651 start,
4652 end: None,
4653 })
4654 }
4655 }
4656
4657 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
4660 if let Token::Integer(n) = *self.peek() {
4662 self.advance();
4663 let n: u64 = u64::try_from(n).map_err(|_| {
4664 self.err(format!(
4665 "invalid frame offset {n} — expected non-negative integer"
4666 ))
4667 })?;
4668 let dir = self.expect_ident_like()?;
4669 return if dir.eq_ignore_ascii_case("preceding") {
4670 Ok(FrameBound::OffsetPreceding(n))
4671 } else if dir.eq_ignore_ascii_case("following") {
4672 Ok(FrameBound::OffsetFollowing(n))
4673 } else {
4674 Err(self.err(format!(
4675 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
4676 )))
4677 };
4678 }
4679 let first = self.expect_ident_like()?;
4680 if first.eq_ignore_ascii_case("unbounded") {
4681 let dir = self.expect_ident_like()?;
4682 return if dir.eq_ignore_ascii_case("preceding") {
4683 Ok(FrameBound::UnboundedPreceding)
4684 } else if dir.eq_ignore_ascii_case("following") {
4685 Ok(FrameBound::UnboundedFollowing)
4686 } else {
4687 Err(self.err(format!(
4688 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
4689 )))
4690 };
4691 }
4692 if first.eq_ignore_ascii_case("current") {
4693 let row = self.expect_ident_like()?;
4694 if !row.eq_ignore_ascii_case("row") {
4695 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
4696 }
4697 return Ok(FrameBound::CurrentRow);
4698 }
4699 Err(self.err(format!(
4700 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
4701 )))
4702 }
4703
4704 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
4705 if matches!(self.peek(), Token::Dot) {
4706 self.advance();
4707 let name = self.expect_ident_like()?;
4708 return Ok(Expr::Column(ColumnName {
4709 qualifier: Some(first),
4710 name,
4711 }));
4712 }
4713 if matches!(self.peek(), Token::LParen) {
4714 self.advance();
4715 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
4719 self.advance();
4720 if !matches!(self.peek(), Token::RParen) {
4721 return Err(self.err(format!(
4722 "expected ')' after COUNT(*), got {:?}",
4723 self.peek()
4724 )));
4725 }
4726 self.advance();
4727 let null_treatment = self.parse_null_treatment_modifier();
4729 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4730 && s.eq_ignore_ascii_case("over")
4731 {
4732 self.advance();
4733 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4734 return Ok(Expr::WindowFunction {
4735 name: "count_star".into(),
4736 args: Vec::new(),
4737 partition_by,
4738 order_by,
4739 frame,
4740 null_treatment,
4741 });
4742 }
4743 return Ok(Expr::FunctionCall {
4744 name: "count_star".into(),
4745 args: Vec::new(),
4746 });
4747 }
4748 let mut args = Vec::new();
4750 if !matches!(self.peek(), Token::RParen) {
4751 loop {
4752 args.push(self.parse_expr(0)?);
4753 match self.peek() {
4754 Token::Comma => {
4755 self.advance();
4756 }
4757 Token::RParen => break,
4758 other => {
4759 return Err(self.err(format!(
4760 "expected ',' or ')' in function args, got {other:?}"
4761 )));
4762 }
4763 }
4764 }
4765 }
4766 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
4774 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4775 && s.eq_ignore_ascii_case("over")
4776 {
4777 self.advance();
4778 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4779 return Ok(Expr::WindowFunction {
4780 name: first,
4781 args,
4782 partition_by,
4783 order_by,
4784 frame,
4785 null_treatment,
4786 });
4787 }
4788 return Ok(Expr::FunctionCall { name: first, args });
4789 }
4790 let lc = first.to_ascii_lowercase();
4796 if matches!(
4797 lc.as_str(),
4798 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
4799 ) {
4800 return Ok(Expr::FunctionCall {
4801 name: lc,
4802 args: Vec::new(),
4803 });
4804 }
4805 Ok(Expr::Column(ColumnName {
4806 qualifier: None,
4807 name: first,
4808 }))
4809 }
4810}
4811
4812fn extract_first_column(expr: &Expr) -> Option<String> {
4820 match expr {
4821 Expr::Column(cn) => Some(cn.name.clone()),
4822 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
4823 Expr::Binary { lhs, rhs, .. } => {
4824 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
4825 }
4826 Expr::Unary { expr: e, .. } => extract_first_column(e),
4827 _ => None,
4828 }
4829}
4830
4831fn maybe_not(expr: Expr, negated: bool) -> Expr {
4832 if negated {
4833 Expr::Unary {
4834 op: UnOp::Not,
4835 expr: Box::new(expr),
4836 }
4837 } else {
4838 expr
4839 }
4840}
4841
4842fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
4843 let pair = match tok {
4844 Token::Or => (BinOp::Or, 1),
4845 Token::And => (BinOp::And, 2),
4846 Token::Eq => (BinOp::Eq, 4),
4847 Token::NotEq => (BinOp::NotEq, 4),
4848 Token::Lt => (BinOp::Lt, 4),
4849 Token::LtEq => (BinOp::LtEq, 4),
4850 Token::Gt => (BinOp::Gt, 4),
4851 Token::GtEq => (BinOp::GtEq, 4),
4852 Token::L2Distance => (BinOp::L2Distance, 5),
4855 Token::InnerProduct => (BinOp::InnerProduct, 5),
4856 Token::CosineDistance => (BinOp::CosineDistance, 5),
4857 Token::Plus => (BinOp::Add, 6),
4858 Token::Minus => (BinOp::Sub, 6),
4859 Token::Concat => (BinOp::Concat, 6),
4862 Token::Star => (BinOp::Mul, 7),
4863 Token::Slash => (BinOp::Div, 7),
4864 Token::JsonGet => (BinOp::JsonGet, 7),
4868 Token::JsonGetText => (BinOp::JsonGetText, 7),
4869 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
4870 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
4871 Token::JsonContains => (BinOp::JsonContains, 7),
4872 Token::TsMatch => (BinOp::TsMatch, 4),
4876 _ => return None,
4877 };
4878 Some(pair)
4879}
4880
4881#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
4882fn extract_numeric_literal(e: &Expr) -> Option<f32> {
4887 match e {
4888 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
4889 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
4890 Expr::Unary {
4891 op: UnOp::Neg,
4892 expr,
4893 } => extract_numeric_literal(expr).map(|x| -x),
4894 _ => None,
4895 }
4896}
4897
4898pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
4906 let parts: Vec<&str> = s.split_whitespace().collect();
4907 if parts.is_empty() || !parts.len().is_multiple_of(2) {
4908 return None;
4909 }
4910 let mut months: i32 = 0;
4911 let mut micros: i64 = 0;
4912 let mut i = 0;
4913 while i < parts.len() {
4914 let n: i64 = parts[i].parse().ok()?;
4915 let unit = parts[i + 1].to_ascii_lowercase();
4916 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
4917 match unit_stripped {
4918 "microsecond" => micros = micros.checked_add(n)?,
4919 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
4920 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
4921 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
4922 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
4923 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
4924 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
4925 "month" => {
4926 let n32 = i32::try_from(n).ok()?;
4927 months = months.checked_add(n32)?;
4928 }
4929 "year" => {
4930 let n32 = i32::try_from(n).ok()?;
4931 months = months.checked_add(n32.checked_mul(12)?)?;
4932 }
4933 _ => return None,
4934 }
4935 i += 2;
4936 }
4937 Some((months, micros))
4938}
4939
4940fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
4951 Some(match ident.to_ascii_lowercase().as_str() {
4952 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
4953 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
4954 "bigint" => ColumnTypeName::BigInt,
4955 "float" | "double" | "real" => ColumnTypeName::Float,
4956 "text" => ColumnTypeName::Text,
4957 "bool" | "boolean" => ColumnTypeName::Bool,
4958 "date" => ColumnTypeName::Date,
4959 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
4960 "timestamptz" => ColumnTypeName::Timestamptz,
4961 "json" => ColumnTypeName::Json,
4962 "jsonb" => ColumnTypeName::Jsonb,
4963 "bytea" | "bytes" => ColumnTypeName::Bytes,
4964 "tsvector" => ColumnTypeName::TsVector,
4965 "tsquery" => ColumnTypeName::TsQuery,
4966 _ => return None,
4967 })
4968}
4969
4970pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
4997 parse_plpgsql_body(body)
4998}
4999
5000fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5001 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
5005 message: alloc::format!("plpgsql body lex error: {e}"),
5006 token_pos: 0,
5007 })?;
5008 let mut parser = Parser::new(tokens);
5009 parser.parse_plpgsql_block()
5010}
5011
5012#[cfg(test)]
5013mod tests {
5014 use super::*;
5015 use alloc::string::ToString;
5016
5017 fn parse(s: &str) -> Statement {
5018 parse_statement(s).expect("parse ok")
5019 }
5020
5021 fn lit_int(n: i64) -> Expr {
5022 Expr::Literal(Literal::Integer(n))
5023 }
5024
5025 fn col(name: &str) -> Expr {
5026 Expr::Column(ColumnName {
5027 qualifier: None,
5028 name: name.into(),
5029 })
5030 }
5031
5032 #[test]
5033 fn select_single_integer() {
5034 let s = parse("SELECT 1");
5035 let Statement::Select(s) = s else {
5036 panic!("expected SELECT")
5037 };
5038 assert_eq!(s.items.len(), 1);
5039 assert!(s.from.is_none());
5040 assert!(s.where_.is_none());
5041 }
5042
5043 #[test]
5044 fn select_multiple_literal_kinds() {
5045 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
5046 let Statement::Select(s) = s else {
5047 panic!("expected SELECT")
5048 };
5049 assert_eq!(s.items.len(), 5);
5050 }
5051
5052 #[test]
5053 fn select_wildcard_from_table() {
5054 let s = parse("SELECT * FROM users");
5055 let Statement::Select(s) = s else {
5056 panic!("expected SELECT")
5057 };
5058 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
5059 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
5060 }
5061
5062 #[test]
5063 fn select_with_table_alias() {
5064 let s = parse("SELECT * FROM users AS u");
5065 let Statement::Select(s) = s else {
5066 panic!("expected SELECT")
5067 };
5068 let t = &s.from.as_ref().unwrap().primary;
5069 assert_eq!(t.name, "users");
5070 assert_eq!(t.alias.as_deref(), Some("u"));
5071 }
5072
5073 #[test]
5074 fn select_with_where_eq() {
5075 let s = parse("SELECT a FROM t WHERE a = 1");
5076 let Statement::Select(s) = s else {
5077 panic!("expected SELECT")
5078 };
5079 let w = s.where_.unwrap();
5080 assert_eq!(
5081 w,
5082 Expr::Binary {
5083 lhs: Box::new(col("a")),
5084 op: BinOp::Eq,
5085 rhs: Box::new(lit_int(1)),
5086 }
5087 );
5088 }
5089
5090 #[test]
5091 fn arithmetic_precedence() {
5092 let s = parse("SELECT 1 + 2 * 3");
5093 let Statement::Select(s) = s else {
5094 panic!("expected SELECT")
5095 };
5096 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5097 panic!("wildcard?")
5098 };
5099 assert_eq!(
5100 expr,
5101 &Expr::Binary {
5102 lhs: Box::new(lit_int(1)),
5103 op: BinOp::Add,
5104 rhs: Box::new(Expr::Binary {
5105 lhs: Box::new(lit_int(2)),
5106 op: BinOp::Mul,
5107 rhs: Box::new(lit_int(3)),
5108 }),
5109 }
5110 );
5111 }
5112
5113 #[test]
5114 fn parentheses_override_precedence() {
5115 let s = parse("SELECT (1 + 2) * 3");
5116 let Statement::Select(s) = s else {
5117 panic!("expected SELECT")
5118 };
5119 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5120 panic!()
5121 };
5122 assert_eq!(
5123 expr,
5124 &Expr::Binary {
5125 lhs: Box::new(Expr::Binary {
5126 lhs: Box::new(lit_int(1)),
5127 op: BinOp::Add,
5128 rhs: Box::new(lit_int(2)),
5129 }),
5130 op: BinOp::Mul,
5131 rhs: Box::new(lit_int(3)),
5132 }
5133 );
5134 }
5135
5136 #[test]
5137 fn not_binds_below_comparison() {
5138 let s = parse("SELECT NOT a = 1 FROM t");
5140 let Statement::Select(s) = s else {
5141 panic!("expected SELECT")
5142 };
5143 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5144 panic!()
5145 };
5146 assert_eq!(
5147 expr,
5148 &Expr::Unary {
5149 op: UnOp::Not,
5150 expr: Box::new(Expr::Binary {
5151 lhs: Box::new(col("a")),
5152 op: BinOp::Eq,
5153 rhs: Box::new(lit_int(1)),
5154 }),
5155 }
5156 );
5157 }
5158
5159 #[test]
5160 fn unary_minus_binds_above_multiplication() {
5161 let s = parse("SELECT -a * 2 FROM t");
5163 let Statement::Select(s) = s else {
5164 panic!("expected SELECT")
5165 };
5166 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5167 panic!()
5168 };
5169 assert_eq!(
5170 expr,
5171 &Expr::Binary {
5172 lhs: Box::new(Expr::Unary {
5173 op: UnOp::Neg,
5174 expr: Box::new(col("a")),
5175 }),
5176 op: BinOp::Mul,
5177 rhs: Box::new(lit_int(2)),
5178 }
5179 );
5180 }
5181
5182 #[test]
5183 fn qualified_column() {
5184 let s = parse("SELECT t.col FROM t");
5185 let Statement::Select(s) = s else {
5186 panic!("expected SELECT")
5187 };
5188 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5189 panic!()
5190 };
5191 assert_eq!(
5192 expr,
5193 &Expr::Column(ColumnName {
5194 qualifier: Some("t".into()),
5195 name: "col".into()
5196 })
5197 );
5198 }
5199
5200 #[test]
5201 fn select_item_alias_with_as() {
5202 let s = parse("SELECT a AS y FROM t");
5203 let Statement::Select(s) = s else {
5204 panic!("expected SELECT")
5205 };
5206 let SelectItem::Expr { alias, .. } = &s.items[0] else {
5207 panic!()
5208 };
5209 assert_eq!(alias.as_deref(), Some("y"));
5210 }
5211
5212 #[test]
5213 fn trailing_semicolon_accepted() {
5214 let s = parse("SELECT 1;");
5215 let Statement::Select(s) = s else {
5216 panic!("expected SELECT")
5217 };
5218 assert_eq!(s.items.len(), 1);
5219 }
5220
5221 #[test]
5222 fn boolean_chain_with_and_or_not() {
5223 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
5225 let Statement::Select(s) = s else {
5226 panic!("expected SELECT")
5227 };
5228 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5229 panic!()
5230 };
5231 let expected = Expr::Binary {
5232 lhs: Box::new(Expr::Unary {
5233 op: UnOp::Not,
5234 expr: Box::new(col("a")),
5235 }),
5236 op: BinOp::Or,
5237 rhs: Box::new(Expr::Binary {
5238 lhs: Box::new(col("b")),
5239 op: BinOp::And,
5240 rhs: Box::new(Expr::Unary {
5241 op: UnOp::Not,
5242 expr: Box::new(col("c")),
5243 }),
5244 }),
5245 };
5246 assert_eq!(expr, &expected);
5247 }
5248
5249 #[test]
5250 fn empty_input_errors() {
5251 let err = parse_statement("").unwrap_err();
5252 assert!(err.message.contains("SELECT"));
5253 }
5254
5255 #[test]
5256 fn unmatched_paren_errors() {
5257 assert!(parse_statement("SELECT (1 + 2").is_err());
5258 }
5259
5260 #[test]
5261 fn display_round_trip_simple_select() {
5262 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
5263 let text = original.to_string();
5264 let again = parse_statement(&text).expect("re-parse");
5265 assert_eq!(original, again);
5266 }
5267
5268 #[test]
5271 fn create_table_single_column() {
5272 let s = parse("CREATE TABLE foo (a INT)");
5273 let Statement::CreateTable(c) = s else {
5274 panic!("expected CreateTable")
5275 };
5276 assert_eq!(c.name, "foo");
5277 assert_eq!(c.columns.len(), 1);
5278 assert_eq!(c.columns[0].name, "a");
5279 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5280 assert!(c.columns[0].nullable);
5281 }
5282
5283 #[test]
5284 fn create_table_multi_column_with_not_null_mix() {
5285 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
5286 let Statement::CreateTable(c) = s else {
5287 panic!()
5288 };
5289 assert_eq!(c.columns.len(), 4);
5290 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5291 assert!(!c.columns[0].nullable);
5292 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
5293 assert!(c.columns[1].nullable);
5294 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
5295 assert!(!c.columns[2].nullable);
5296 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
5297 }
5298
5299 #[test]
5300 fn create_table_bigint_supported() {
5301 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
5302 let Statement::CreateTable(c) = s else {
5303 panic!()
5304 };
5305 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
5306 }
5307
5308 #[test]
5309 fn create_table_vector_default_is_f32() {
5310 let s = parse("CREATE TABLE t (v VECTOR(128))");
5311 let Statement::CreateTable(c) = s else {
5312 panic!()
5313 };
5314 assert_eq!(
5315 c.columns[0].ty,
5316 ColumnTypeName::Vector {
5317 dim: 128,
5318 encoding: VecEncoding::F32,
5319 },
5320 );
5321 }
5322
5323 #[test]
5324 fn create_table_vector_using_sq8() {
5325 for sql in [
5328 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
5329 "CREATE TABLE t (v VECTOR(128) using sq8)",
5330 ] {
5331 let s = parse(sql);
5332 let Statement::CreateTable(c) = s else {
5333 panic!()
5334 };
5335 assert_eq!(
5336 c.columns[0].ty,
5337 ColumnTypeName::Vector {
5338 dim: 128,
5339 encoding: VecEncoding::Sq8,
5340 },
5341 "{sql}",
5342 );
5343 }
5344 }
5345
5346 #[test]
5347 fn create_table_vector_using_unknown_errors() {
5348 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
5349 assert!(
5350 err.message.contains("unknown vector encoding"),
5351 "got: {}",
5352 err.message
5353 );
5354 }
5355
5356 #[test]
5357 fn vector_using_sq8_display_roundtrips() {
5358 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
5361 let Statement::CreateTable(c) = s else {
5362 panic!()
5363 };
5364 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
5365 }
5366
5367 #[test]
5368 fn parser_recognises_placeholders() {
5369 use crate::ast::{Expr, SelectItem, Statement};
5370 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
5372 let Statement::Select(sel) = s else { panic!() };
5373 assert!(matches!(
5374 sel.items[0],
5375 SelectItem::Expr {
5376 expr: Expr::Placeholder(1),
5377 alias: None
5378 }
5379 ));
5380 let SelectItem::Expr {
5382 expr: Expr::Binary { lhs, rhs, .. },
5383 ..
5384 } = &sel.items[1]
5385 else {
5386 panic!()
5387 };
5388 assert!(matches!(**lhs, Expr::Placeholder(2)));
5389 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
5390 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
5392 panic!()
5393 };
5394 assert!(matches!(**rhs, Expr::Placeholder(3)));
5395 }
5396
5397 #[test]
5398 fn parser_rejects_dollar_zero() {
5399 assert!(parse_statement("SELECT $0").is_err());
5401 }
5402
5403 #[test]
5404 fn placeholder_display_roundtrips() {
5405 let s = parse("SELECT $42 FROM t");
5408 let printed = s.to_string();
5409 assert!(printed.contains("$42"));
5410 let again = parse(&printed);
5411 assert_eq!(s, again);
5412 }
5413
5414 #[test]
5415 fn alter_index_rebuild_bare() {
5416 use crate::ast::{AlterIndexTarget, Statement};
5417 let s = parse("ALTER INDEX my_idx REBUILD");
5418 let Statement::AlterIndex(a) = s else {
5419 panic!("expected AlterIndex, got {s:?}")
5420 };
5421 assert_eq!(a.name, "my_idx");
5422 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
5423 }
5424
5425 #[test]
5426 fn alter_index_rebuild_with_encoding() {
5427 use crate::ast::{AlterIndexTarget, Statement};
5428 for (sql, want) in [
5429 (
5430 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
5431 VecEncoding::F32,
5432 ),
5433 (
5434 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
5435 VecEncoding::Sq8,
5436 ),
5437 (
5438 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5439 VecEncoding::F16,
5440 ),
5441 ] {
5442 let s = parse(sql);
5443 let Statement::AlterIndex(a) = s else {
5444 panic!("{sql}: expected AlterIndex")
5445 };
5446 assert_eq!(a.name, "my_idx");
5447 assert_eq!(
5448 a.target,
5449 AlterIndexTarget::Rebuild {
5450 encoding: Some(want)
5451 },
5452 "{sql}"
5453 );
5454 }
5455 }
5456
5457 #[test]
5458 fn alter_index_rebuild_unknown_encoding_errors() {
5459 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
5460 assert!(
5461 err.message.contains("unknown vector encoding"),
5462 "got: {}",
5463 err.message
5464 );
5465 }
5466
5467 #[test]
5468 fn alter_index_rebuild_display_roundtrips() {
5469 for (input, want) in [
5470 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
5471 (
5472 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5473 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5474 ),
5475 (
5476 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5477 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5478 ),
5479 ] {
5480 let s = parse(input);
5481 assert_eq!(s.to_string(), want);
5482 }
5483 }
5484
5485 #[test]
5486 fn create_table_unknown_type_errors() {
5487 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
5490 assert!(err.message.contains("unsupported column type"));
5491 }
5492
5493 #[test]
5494 fn create_table_missing_table_keyword_errors() {
5495 assert!(parse_statement("CREATE x (a INT)").is_err());
5496 }
5497
5498 #[test]
5499 fn insert_single_value() {
5500 let s = parse("INSERT INTO foo VALUES (42)");
5501 let Statement::Insert(i) = s else {
5502 panic!("expected Insert")
5503 };
5504 assert_eq!(i.table, "foo");
5505 assert_eq!(i.rows.len(), 1);
5506 assert_eq!(i.rows[0].len(), 1);
5507 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
5508 }
5509
5510 #[test]
5511 fn insert_multi_value_with_mixed_literals() {
5512 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
5513 let Statement::Insert(i) = s else { panic!() };
5514 assert_eq!(i.rows.len(), 1);
5515 assert_eq!(i.rows[0].len(), 5);
5516 }
5517
5518 #[test]
5519 fn insert_missing_into_errors() {
5520 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
5521 }
5522
5523 #[test]
5524 fn create_table_round_trip() {
5525 let original =
5526 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
5527 let text = original.to_string();
5528 let again = parse_statement(&text).expect("re-parse");
5529 assert_eq!(original, again);
5530 }
5531
5532 #[test]
5533 fn insert_round_trip_with_negation_and_string() {
5534 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
5535 let text = original.to_string();
5536 let again = parse_statement(&text).expect("re-parse");
5537 assert_eq!(original, again);
5538 }
5539
5540 #[test]
5541 fn unknown_keyword_at_statement_start_errors() {
5542 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
5545 assert!(err.message.contains("expected SELECT"));
5546 }
5547
5548 #[test]
5551 fn create_index_basic() {
5552 let s = parse("CREATE INDEX idx_id ON users (id)");
5553 let Statement::CreateIndex(c) = s else {
5554 panic!("expected CreateIndex")
5555 };
5556 assert_eq!(c.name, "idx_id");
5557 assert_eq!(c.table, "users");
5558 assert_eq!(c.column, "id");
5559 }
5560
5561 #[test]
5562 fn create_index_missing_on_errors() {
5563 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
5564 }
5565
5566 #[test]
5567 fn create_index_missing_paren_errors() {
5568 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
5569 }
5570
5571 #[test]
5572 fn create_index_round_trip() {
5573 let original = parse("CREATE INDEX by_name ON users (name)");
5574 let again = parse_statement(&original.to_string()).unwrap();
5575 assert_eq!(original, again);
5576 }
5577
5578 #[test]
5581 fn create_unique_index_basic() {
5582 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
5583 let Statement::CreateIndex(c) = s else {
5584 panic!("expected CreateIndex");
5585 };
5586 assert!(c.is_unique);
5587 assert_eq!(c.column, "a");
5588 assert!(c.partial_predicate.is_none());
5589 }
5590
5591 #[test]
5592 fn create_unique_index_partial() {
5593 let s = parse(
5595 "CREATE UNIQUE INDEX idx_email_templates_user_default \
5596 ON email_templates (user_address) WHERE is_default = true",
5597 );
5598 let Statement::CreateIndex(c) = s else {
5599 panic!("expected CreateIndex");
5600 };
5601 assert!(c.is_unique);
5602 assert_eq!(c.table, "email_templates");
5603 assert_eq!(c.column, "user_address");
5604 assert!(c.partial_predicate.is_some());
5605 }
5606
5607 #[test]
5608 fn create_unique_index_composite_with_predicate() {
5609 let s = parse(
5611 "CREATE UNIQUE INDEX uq_calendar_events_instance \
5612 ON calendar_events (calendar_id, uid, recurrence_id) \
5613 WHERE recurrence_id IS NOT NULL",
5614 );
5615 let Statement::CreateIndex(c) = s else {
5616 panic!("expected CreateIndex");
5617 };
5618 assert!(c.is_unique);
5619 assert_eq!(c.column, "calendar_id");
5620 assert_eq!(
5621 c.extra_columns,
5622 vec!["uid".to_string(), "recurrence_id".to_string()]
5623 );
5624 assert!(c.partial_predicate.is_some());
5625 }
5626
5627 #[test]
5628 fn create_unique_index_using_btree_ok() {
5629 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
5630 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
5631 }
5632
5633 #[test]
5634 fn create_unique_index_using_hnsw_rejected() {
5635 let err =
5636 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
5637 assert!(err.message.contains("UNIQUE"), "{}", err.message);
5638 }
5639
5640 #[test]
5641 fn create_unique_index_round_trip() {
5642 let original = parse(
5643 "CREATE UNIQUE INDEX uq_calendar_events_master \
5644 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
5645 );
5646 let again = parse_statement(&original.to_string()).unwrap();
5647 assert_eq!(original, again);
5648 }
5649
5650 #[test]
5651 fn create_unique_without_index_errors() {
5652 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
5653 assert!(err.message.contains("INDEX"), "{}", err.message);
5654 }
5655
5656 #[test]
5659 fn create_table_bytea_column() {
5660 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
5661 let Statement::CreateTable(c) = s else {
5662 panic!("expected CreateTable");
5663 };
5664 assert_eq!(c.columns.len(), 2);
5665 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
5666 assert!(!c.columns[1].nullable);
5667 }
5668
5669 #[test]
5670 fn create_table_bytes_alias_column() {
5671 let s = parse("CREATE TABLE t (blob BYTES)");
5672 let Statement::CreateTable(c) = s else {
5673 panic!("expected CreateTable");
5674 };
5675 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
5676 }
5677
5678 #[test]
5679 fn bytea_round_trip_display() {
5680 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
5681 let again = parse_statement(&original.to_string()).unwrap();
5682 assert_eq!(original, again);
5683 }
5684
5685 #[test]
5688 fn begin_commit_rollback_parse_as_unit_variants() {
5689 assert_eq!(parse("BEGIN"), Statement::Begin);
5690 assert_eq!(parse("COMMIT"), Statement::Commit);
5691 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
5692 assert_eq!(parse("BEGIN;"), Statement::Begin);
5694 }
5695
5696 #[test]
5699 fn inner_product_binop_parses() {
5700 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
5701 let Statement::Select(s) = s else { panic!() };
5702 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5703 panic!()
5704 };
5705 assert!(matches!(
5706 expr,
5707 Expr::Binary {
5708 op: BinOp::InnerProduct,
5709 ..
5710 }
5711 ));
5712 }
5713
5714 #[test]
5715 fn cosine_distance_binop_parses() {
5716 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
5717 let Statement::Select(s) = s else { panic!() };
5718 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5719 panic!()
5720 };
5721 assert!(matches!(
5722 expr,
5723 Expr::Binary {
5724 op: BinOp::CosineDistance,
5725 ..
5726 }
5727 ));
5728 }
5729
5730 #[test]
5731 fn vector_cast_postfix_wraps_string_literal() {
5732 let s = parse("SELECT '[1,2,3]'::vector FROM t");
5733 let Statement::Select(s) = s else { panic!() };
5734 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5735 panic!()
5736 };
5737 assert!(matches!(
5738 expr,
5739 Expr::Cast {
5740 target: CastTarget::Vector,
5741 ..
5742 }
5743 ));
5744 }
5745
5746 #[test]
5747 fn unsupported_cast_target_errors() {
5748 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
5750 assert!(err.message.contains("unsupported cast target"));
5751 }
5752
5753 #[test]
5754 fn tx_statements_round_trip() {
5755 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
5756 let original = parse(q);
5757 let again = parse_statement(&original.to_string()).unwrap();
5758 assert_eq!(original, again);
5759 }
5760 }
5761
5762 #[test]
5763 fn interval_text_parsing_units() {
5764 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
5766 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
5767 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
5768 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
5769 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
5771 assert_eq!(
5772 parse_interval_text("1 day 2 hours"),
5773 Some((0, 86_400_000_000 + 7_200_000_000))
5774 );
5775 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
5777 assert_eq!(parse_interval_text(""), None);
5779 assert_eq!(parse_interval_text("garbage"), None);
5780 assert_eq!(parse_interval_text("1 fortnight"), None);
5781 assert_eq!(parse_interval_text("1"), None);
5782 }
5783
5784 #[test]
5785 fn interval_literal_roundtrips_via_display() {
5786 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
5787 let s = parsed.to_string();
5788 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
5790 let again = parse_statement(&s).unwrap();
5792 assert_eq!(parsed, again);
5793 }
5794
5795 #[test]
5798 fn parser_recognises_create_publication_bare() {
5799 let s = parse("CREATE PUBLICATION pub_a");
5800 let Statement::CreatePublication(p) = s else {
5801 panic!("expected CreatePublication, got {s:?}")
5802 };
5803 assert_eq!(p.name, "pub_a");
5804 assert_eq!(p.scope, PublicationScope::AllTables);
5805 }
5806
5807 #[test]
5808 fn parser_recognises_create_publication_for_all_tables() {
5809 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
5810 let Statement::CreatePublication(p) = s else {
5811 panic!("expected CreatePublication, got {s:?}")
5812 };
5813 assert_eq!(p.name, "pub_a");
5814 assert_eq!(p.scope, PublicationScope::AllTables);
5815 }
5816
5817 #[test]
5818 fn parser_recognises_drop_publication() {
5819 let s = parse("DROP PUBLICATION pub_a");
5820 let Statement::DropPublication(name) = s else {
5821 panic!("expected DropPublication, got {s:?}")
5822 };
5823 assert_eq!(name, "pub_a");
5824 }
5825
5826 #[test]
5827 fn parser_recognises_for_table_list() {
5828 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
5829 let Statement::CreatePublication(p) = s else {
5830 panic!("expected CreatePublication, got {s:?}")
5831 };
5832 assert_eq!(p.name, "pub_a");
5833 let PublicationScope::ForTables(ts) = p.scope else {
5834 panic!("expected ForTables scope")
5835 };
5836 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
5837 }
5838
5839 #[test]
5840 fn parser_recognises_for_tables_plural() {
5841 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
5843 let Statement::CreatePublication(p) = s else {
5844 panic!("expected CreatePublication, got {s:?}")
5845 };
5846 let PublicationScope::ForTables(ts) = p.scope else {
5847 panic!("expected ForTables")
5848 };
5849 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5850 }
5851
5852 #[test]
5853 fn parser_recognises_for_all_tables_except_list() {
5854 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
5855 let Statement::CreatePublication(p) = s else {
5856 panic!()
5857 };
5858 let PublicationScope::AllTablesExcept(ts) = p.scope else {
5859 panic!("expected AllTablesExcept")
5860 };
5861 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5862 }
5863
5864 #[test]
5865 fn parser_rejects_for_table_with_empty_list() {
5866 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
5868 .expect_err("must error on empty list");
5869 assert!(!err.message.is_empty());
5872 }
5873
5874 #[test]
5875 fn parser_recognises_show_publications() {
5876 let s = parse("SHOW PUBLICATIONS");
5879 assert!(matches!(s, Statement::ShowPublications));
5880 }
5881
5882 #[test]
5885 fn parser_recognises_create_subscription_single_publication() {
5886 let s = parse(
5887 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
5888 );
5889 let Statement::CreateSubscription(c) = s else {
5890 panic!("expected CreateSubscription, got {s:?}")
5891 };
5892 assert_eq!(c.name, "sub_a");
5893 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
5894 assert_eq!(c.publications, alloc::vec!["pub_a"]);
5895 }
5896
5897 #[test]
5898 fn parser_recognises_create_subscription_multi_publication() {
5899 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
5900 let Statement::CreateSubscription(c) = s else {
5901 panic!()
5902 };
5903 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
5904 }
5905
5906 #[test]
5907 fn parser_rejects_create_subscription_missing_connection() {
5908 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
5909 .expect_err("must error on missing CONNECTION");
5910 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
5911 }
5912
5913 #[test]
5914 fn parser_rejects_create_subscription_missing_publication() {
5915 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
5916 .expect_err("must error on missing PUBLICATION");
5917 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
5918 }
5919
5920 #[test]
5921 fn parser_recognises_drop_subscription() {
5922 let s = parse("DROP SUBSCRIPTION sub_a");
5923 let Statement::DropSubscription(name) = s else {
5924 panic!("expected DropSubscription, got {s:?}")
5925 };
5926 assert_eq!(name, "sub_a");
5927 }
5928
5929 #[test]
5930 fn parser_recognises_show_subscriptions() {
5931 let s = parse("SHOW SUBSCRIPTIONS");
5932 assert!(matches!(s, Statement::ShowSubscriptions));
5933 }
5934
5935 #[test]
5936 fn parser_recognises_wait_for_wal_position_no_timeout() {
5937 let s = parse("WAIT FOR WAL POSITION 12345");
5938 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
5939 panic!("expected WaitForWalPosition, got {s:?}")
5940 };
5941 assert_eq!(pos, 12345);
5942 assert!(timeout_ms.is_none());
5943 }
5944
5945 #[test]
5946 fn parser_recognises_wait_for_wal_position_with_timeout() {
5947 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
5948 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
5949 panic!()
5950 };
5951 assert_eq!(pos, 67890);
5952 assert_eq!(timeout_ms, Some(5000));
5953 }
5954
5955 #[test]
5956 fn parser_rejects_wait_with_negative_position() {
5957 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
5963 assert!(!err.message.is_empty());
5964 }
5965
5966 #[test]
5967 fn parser_recognises_bare_analyze() {
5968 let s = parse("ANALYZE");
5969 assert!(matches!(s, Statement::Analyze(None)));
5970 }
5971
5972 #[test]
5973 fn parser_recognises_analyze_with_table() {
5974 let s = parse("ANALYZE users");
5975 let Statement::Analyze(Some(name)) = s else {
5976 panic!("expected Analyze, got {s:?}")
5977 };
5978 assert_eq!(name, "users");
5979 }
5980
5981 #[test]
5982 fn parser_recognises_analyze_with_quoted_table() {
5983 let s = parse("ANALYZE \"Mixed Case\"");
5984 let Statement::Analyze(Some(name)) = s else {
5985 panic!()
5986 };
5987 assert_eq!(name, "Mixed Case");
5988 }
5989
5990 #[test]
5991 fn parser_rejects_analyze_with_garbage_token() {
5992 let err = parse_statement("ANALYZE 42").expect_err("must error");
5993 assert!(!err.message.is_empty());
5994 }
5995
5996 #[test]
5997 fn analyze_display_roundtrips() {
5998 for sql in ["ANALYZE", "ANALYZE users"] {
5999 let s = parse(sql);
6000 let printed = s.to_string();
6001 let again = parse_statement(&printed)
6002 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6003 assert_eq!(s, again);
6004 }
6005 }
6006
6007 #[test]
6008 fn wait_for_display_roundtrips() {
6009 for sql in [
6010 "WAIT FOR WAL POSITION 12345",
6011 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
6012 ] {
6013 let s = parse(sql);
6014 let printed = s.to_string();
6015 let again = parse_statement(&printed)
6016 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6017 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6018 }
6019 }
6020
6021 #[test]
6022 fn subscription_ddl_display_roundtrips() {
6023 for sql in [
6024 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
6025 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
6026 "DROP SUBSCRIPTION sub_a",
6027 "SHOW SUBSCRIPTIONS",
6028 ] {
6029 let s = parse(sql);
6030 let printed = s.to_string();
6031 let again = parse_statement(&printed)
6032 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6033 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6034 }
6035 }
6036
6037 #[test]
6038 fn parser_drop_dispatches_user_vs_publication() {
6039 let s = parse("DROP USER 'alice'");
6042 let Statement::DropUser(name) = s else {
6043 panic!("expected DropUser, got {s:?}")
6044 };
6045 assert_eq!(name, "alice");
6046 let s = parse("DROP PUBLICATION p1");
6048 assert!(matches!(s, Statement::DropPublication(_)));
6049 }
6050
6051 #[test]
6052 fn publication_ddl_display_roundtrips() {
6053 for sql in [
6056 "CREATE PUBLICATION pub_a",
6057 "CREATE PUBLICATION pub_a FOR ALL TABLES",
6058 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
6059 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
6060 "DROP PUBLICATION pub_a",
6061 "SHOW PUBLICATIONS",
6062 ] {
6063 let s = parse(sql);
6064 let printed = s.to_string();
6065 let again = parse_statement(&printed)
6066 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6067 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6068 }
6069 }
6070
6071 #[test]
6074 fn create_function_returns_trigger_plpgsql_minimal() {
6075 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
6076 let s = parse(sql);
6077 let Statement::CreateFunction(f) = s else {
6078 panic!("expected CreateFunction");
6079 };
6080 assert_eq!(f.name, "noop");
6081 assert!(!f.or_replace);
6082 assert!(f.args.is_empty());
6083 assert!(matches!(f.returns, FunctionReturn::Trigger));
6084 assert_eq!(f.language, "plpgsql");
6085 let FunctionBody::PlPgSql(block) = f.body else {
6086 panic!("expected PlPgSql body");
6087 };
6088 assert_eq!(block.statements.len(), 1);
6089 assert!(matches!(
6090 block.statements[0],
6091 PlPgSqlStmt::Return(ReturnTarget::New)
6092 ));
6093 }
6094
6095 #[test]
6096 fn create_function_or_replace_with_assignment() {
6097 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
6100BEGIN
6101 NEW.search_vector := to_tsvector('english', NEW.subject);
6102 RETURN NEW;
6103END;
6104$$";
6105 let s = parse(sql);
6106 let Statement::CreateFunction(f) = s else {
6107 panic!("expected CreateFunction");
6108 };
6109 assert!(f.or_replace);
6110 let FunctionBody::PlPgSql(block) = &f.body else {
6111 panic!("expected PlPgSql body");
6112 };
6113 assert_eq!(block.statements.len(), 2);
6114 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
6116 panic!("expected Assign as first stmt");
6117 };
6118 match target {
6119 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
6120 other => panic!("expected NEW.col, got {other:?}"),
6121 }
6122 assert!(matches!(
6124 block.statements[1],
6125 PlPgSqlStmt::Return(ReturnTarget::New)
6126 ));
6127 }
6128
6129 #[test]
6130 fn create_trigger_after_insert_or_update() {
6131 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
6132 let s = parse(sql);
6133 let Statement::CreateTrigger(t) = s else {
6134 panic!("expected CreateTrigger");
6135 };
6136 assert_eq!(t.name, "tg");
6137 assert_eq!(t.table, "messages");
6138 assert_eq!(t.timing, TriggerTiming::After);
6139 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
6140 assert_eq!(t.for_each, TriggerForEach::Row);
6141 assert_eq!(t.function, "update_sv");
6142 }
6143
6144 #[test]
6145 fn create_trigger_before_delete_execute_procedure_alias() {
6146 let sql =
6148 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
6149 let s = parse(sql);
6150 let Statement::CreateTrigger(t) = s else {
6151 panic!("expected CreateTrigger");
6152 };
6153 assert_eq!(t.timing, TriggerTiming::Before);
6154 assert_eq!(t.events, vec![TriggerEvent::Delete]);
6155 }
6156
6157 #[test]
6158 fn drop_trigger_if_exists_round_trips() {
6159 let s = Statement::DropTrigger {
6164 name: "tg".into(),
6165 table: "messages".into(),
6166 if_exists: true,
6167 };
6168 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
6169 }
6170
6171 #[test]
6172 fn trigger_ddl_display_roundtrips_through_parser() {
6173 for sql in [
6177 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
6178 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
6179 ] {
6180 let s = parse(sql);
6181 let printed = s.to_string();
6182 let again = parse_statement(&printed)
6183 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6184 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6185 }
6186 }
6187}