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> {
1776 let table_name = self.expect_ident_like()?;
1777 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
1778 loop {
1779 let subaction = self.parse_alter_table_subaction()?;
1780 targets.extend(subaction);
1784 if matches!(self.peek(), Token::Comma) {
1785 self.advance();
1786 continue;
1787 }
1788 break;
1789 }
1790 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
1791 name: table_name,
1792 targets,
1793 }))
1794 }
1795
1796 fn parse_alter_table_subaction(
1800 &mut self,
1801 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
1802 match self.peek() {
1803 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
1804 self.advance();
1805 let setting = self.expect_ident_like()?;
1806 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
1807 return Err(self.err(alloc::format!(
1808 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
1809 )));
1810 }
1811 if !matches!(self.peek(), Token::Eq) {
1812 return Err(self.err(alloc::format!(
1813 "expected '=' after hot_tier_bytes, got {:?}",
1814 self.peek()
1815 )));
1816 }
1817 self.advance();
1818 let n = self.expect_u64_literal()?;
1819 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
1820 }
1821 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
1822 self.advance();
1823 let is_fk = matches!(
1824 self.peek(),
1825 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
1826 || s.eq_ignore_ascii_case("foreign")
1827 );
1828 if is_fk {
1829 let fk = self.parse_table_level_fk()?;
1830 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
1831 }
1832 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
1833 self.advance();
1834 }
1835 let mut if_not_exists = false;
1836 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
1837 self.advance();
1838 if !matches!(self.peek(), Token::Not) {
1839 return Err(self.err(alloc::format!(
1840 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
1841 self.peek()
1842 )));
1843 }
1844 self.advance();
1845 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
1846 return Err(self.err(alloc::format!(
1847 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
1848 self.peek()
1849 )));
1850 }
1851 self.advance();
1852 if_not_exists = true;
1853 }
1854 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
1858 let col_name = column.name.clone();
1859 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
1860 column,
1861 if_not_exists,
1862 }];
1863 if let Some(mut fk) = col_level_fk {
1864 if fk.columns.is_empty() {
1865 fk.columns.push(col_name);
1866 }
1867 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
1868 }
1869 Ok(out)
1870 }
1871 Token::Drop => {
1872 self.advance();
1873 let subject = match self.peek() {
1880 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
1881 self.advance();
1882 "constraint"
1883 }
1884 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
1885 self.advance();
1886 "column"
1887 }
1888 Token::Ident(_) | Token::QuotedIdent(_) => "column",
1892 other => {
1893 return Err(self.err(alloc::format!(
1894 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
1895 )));
1896 }
1897 };
1898 let mut if_exists = false;
1899 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
1900 let n1 = self.tokens.get(self.pos + 1);
1901 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
1902 self.advance();
1903 self.advance();
1904 if_exists = true;
1905 }
1906 }
1907 let name = self.expect_ident_like()?;
1908 let mut cascade = false;
1909 if matches!(
1910 self.peek(),
1911 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
1912 || s.eq_ignore_ascii_case("restrict")
1913 ) {
1914 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
1915 {
1916 cascade = true;
1917 }
1918 self.advance();
1919 }
1920 if subject == "constraint" {
1921 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
1922 name,
1923 if_exists,
1924 }])
1925 } else {
1926 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
1927 column: name,
1928 if_exists,
1929 cascade,
1930 }])
1931 }
1932 }
1933 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
1934 self.advance();
1935 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
1936 self.advance();
1937 }
1938 let col_name = self.expect_ident_like()?;
1939 match self.peek() {
1940 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
1941 self.advance();
1942 }
1943 other => {
1944 return Err(self.err(alloc::format!(
1945 "expected TYPE after ALTER COLUMN <name>, got {other:?}"
1946 )));
1947 }
1948 }
1949 let new_type = self.parse_column_type_name()?;
1950 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
1951 {
1952 self.advance();
1953 Some(self.parse_expr(0)?)
1954 } else {
1955 None
1956 };
1957 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
1958 column: col_name,
1959 new_type,
1960 using,
1961 }])
1962 }
1963 other => Err(self.err(alloc::format!(
1964 "expected SET / ADD / DROP / ALTER in ALTER TABLE, got {other:?}"
1965 ))),
1966 }
1967 }
1968
1969 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
1971 match self.advance() {
1972 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
1973 other => Err(ParseError {
1974 message: format!("expected {kw:?}, got {other:?}"),
1975 token_pos: self.pos.saturating_sub(1),
1976 }),
1977 }
1978 }
1979
1980 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
1984 match self.advance() {
1985 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
1986 other => Err(ParseError {
1987 message: format!("expected identifier or string, got {other:?}"),
1988 token_pos: self.pos.saturating_sub(1),
1989 }),
1990 }
1991 }
1992
1993 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
1994 match self.advance() {
1995 Token::String(s) => Ok(s),
1996 other => Err(ParseError {
1997 message: format!("expected quoted string, got {other:?}"),
1998 token_pos: self.pos.saturating_sub(1),
1999 }),
2000 }
2001 }
2002
2003 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
2004 let mut head = self.parse_bare_select()?;
2009 while matches!(self.peek(), Token::Union) {
2010 self.advance();
2011 let kind = if matches!(self.peek(), Token::All) {
2012 self.advance();
2013 UnionKind::All
2014 } else {
2015 UnionKind::Distinct
2016 };
2017 let peer = self.parse_bare_select()?;
2018 head.unions.push((kind, peer));
2019 }
2020 head.order_by = if matches!(self.peek(), Token::Order) {
2021 self.advance();
2022 if !matches!(self.peek(), Token::By) {
2023 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
2024 }
2025 self.advance();
2026 let mut keys = Vec::new();
2029 loop {
2030 let expr = self.parse_expr(0)?;
2031 let desc = if matches!(self.peek(), Token::Desc) {
2032 self.advance();
2033 true
2034 } else if matches!(self.peek(), Token::Asc) {
2035 self.advance();
2036 false
2037 } else {
2038 false
2039 };
2040 keys.push(OrderBy { expr, desc });
2041 if matches!(self.peek(), Token::Comma) {
2042 self.advance();
2043 } else {
2044 break;
2045 }
2046 }
2047 keys
2048 } else {
2049 Vec::new()
2050 };
2051 head.limit = if matches!(self.peek(), Token::Limit) {
2052 self.advance();
2053 Some(self.parse_limit_expr("LIMIT")?)
2054 } else {
2055 None
2056 };
2057 head.offset = if matches!(self.peek(), Token::Offset) {
2058 self.advance();
2059 Some(self.parse_limit_expr("OFFSET")?)
2060 } else {
2061 None
2062 };
2063 Ok(Statement::Select(head))
2064 }
2065
2066 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2071 match self.advance() {
2072 Token::Integer(n) if n >= 0 => u32::try_from(n)
2073 .map(crate::ast::LimitExpr::Literal)
2074 .map_err(|_| ParseError {
2075 message: alloc::format!("{label} value too large: {n}"),
2076 token_pos: self.pos.saturating_sub(1),
2077 }),
2078 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2079 other => Err(ParseError {
2080 message: alloc::format!(
2081 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2082 ),
2083 token_pos: self.pos.saturating_sub(1),
2084 }),
2085 }
2086 }
2087
2088 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2093 if !matches!(self.peek(), Token::Select) {
2094 return Err(self.err(format!(
2095 "expected SELECT to start a query block, got {:?}",
2096 self.peek()
2097 )));
2098 }
2099 self.advance();
2100 let distinct = if matches!(self.peek(), Token::Distinct) {
2101 self.advance();
2102 true
2103 } else {
2104 false
2105 };
2106 let items = self.parse_select_list()?;
2107 let from = if matches!(self.peek(), Token::From) {
2108 self.advance();
2109 Some(self.parse_from_clause()?)
2110 } else {
2111 None
2112 };
2113 let where_ = if matches!(self.peek(), Token::Where) {
2114 self.advance();
2115 Some(self.parse_expr(0)?)
2116 } else {
2117 None
2118 };
2119 let mut group_by_all = false;
2120 let group_by = if matches!(self.peek(), Token::Group) {
2121 self.advance();
2122 if !matches!(self.peek(), Token::By) {
2123 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2124 }
2125 self.advance();
2126 if matches!(self.peek(), Token::All) {
2129 self.advance();
2130 group_by_all = true;
2131 None
2132 } else {
2133 let mut groups = Vec::new();
2134 loop {
2135 groups.push(self.parse_expr(0)?);
2136 if matches!(self.peek(), Token::Comma) {
2137 self.advance();
2138 } else {
2139 break;
2140 }
2141 }
2142 Some(groups)
2143 }
2144 } else {
2145 None
2146 };
2147 let having = if matches!(self.peek(), Token::Having) {
2148 self.advance();
2149 Some(self.parse_expr(0)?)
2150 } else {
2151 None
2152 };
2153 Ok(SelectStatement {
2154 ctes: Vec::new(),
2155 distinct,
2156 items,
2157 from,
2158 where_,
2159 group_by,
2160 group_by_all,
2161 having,
2162 unions: Vec::new(),
2163 order_by: Vec::new(),
2164 limit: None,
2165 offset: None,
2166 })
2167 }
2168
2169 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2170 debug_assert!(matches!(self.peek(), Token::Table));
2172 self.advance();
2173 let if_not_exists = self.consume_if_not_exists();
2174 let name = self.expect_ident_like()?;
2175 if !matches!(self.peek(), Token::LParen) {
2176 return Err(self.err(format!(
2177 "expected '(' after table name, got {:?}",
2178 self.peek()
2179 )));
2180 }
2181 self.advance();
2182 let mut columns = Vec::new();
2183 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
2184 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
2185 loop {
2186 if self.peek_table_level_pk_start() {
2192 table_constraints.push(self.parse_table_level_primary_key()?);
2193 } else if self.peek_table_level_unique_start() {
2194 table_constraints.push(self.parse_table_level_unique()?);
2195 } else if self.peek_table_level_check_start() {
2196 table_constraints.push(self.parse_table_level_check()?);
2198 } else if self.peek_constraint_or_fk_start() {
2199 foreign_keys.push(self.parse_table_level_fk()?);
2200 } else {
2201 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
2202 if col.is_unique {
2206 table_constraints.push(crate::ast::TableConstraint::Unique {
2207 name: None,
2208 columns: alloc::vec![col.name.clone()],
2209 nulls_not_distinct: false,
2210 });
2211 }
2212 if let Some(check_expr) = col.check.clone() {
2213 table_constraints.push(crate::ast::TableConstraint::Check {
2214 name: None,
2215 expr: check_expr,
2216 });
2217 }
2218 columns.push(col);
2219 if let Some(fk) = col_level_fk {
2220 foreign_keys.push(fk);
2221 }
2222 }
2223 match self.peek() {
2224 Token::Comma => {
2225 self.advance();
2226 }
2227 Token::RParen => {
2228 self.advance();
2229 break;
2230 }
2231 other => {
2232 return Err(
2233 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2234 );
2235 }
2236 }
2237 }
2238 if columns.is_empty() {
2239 return Err(self.err("CREATE TABLE requires at least one column".into()));
2240 }
2241 Ok(Statement::CreateTable(CreateTableStatement {
2242 name,
2243 columns,
2244 if_not_exists,
2245 foreign_keys,
2246 table_constraints,
2247 }))
2248 }
2249
2250 fn peek_table_level_pk_start(&self) -> bool {
2255 let cur = self.peek();
2256 let nxt = self.tokens.get(self.pos + 1);
2257 let nxt2 = self.tokens.get(self.pos + 2);
2258 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
2259 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
2260 let is_lparen = matches!(nxt2, Some(Token::LParen));
2261 is_primary && is_key && is_lparen
2262 }
2263
2264 fn peek_table_level_unique_start(&self) -> bool {
2268 let cur = self.peek();
2269 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
2270 if !is_unique {
2271 return false;
2272 }
2273 let n1 = self.tokens.get(self.pos + 1);
2274 if matches!(n1, Some(Token::LParen)) {
2276 return true;
2277 }
2278 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
2280 if !is_nulls {
2281 return false;
2282 }
2283 let n2 = self.tokens.get(self.pos + 2);
2284 let n3 = self.tokens.get(self.pos + 3);
2285 let n4 = self.tokens.get(self.pos + 4);
2286 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
2288 return true;
2289 }
2290 if matches!(n2, Some(Token::Not))
2292 && matches!(n3, Some(Token::Distinct))
2293 && matches!(n4, Some(Token::LParen))
2294 {
2295 return true;
2296 }
2297 false
2298 }
2299
2300 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2301 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
2304 Ok(crate::ast::TableConstraint::PrimaryKey {
2305 name: None,
2306 columns,
2307 })
2308 }
2309
2310 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2311 self.advance(); let mut nulls_not_distinct = false;
2316 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
2317 let n1 = self.tokens.get(self.pos + 1);
2318 let n2 = self.tokens.get(self.pos + 2);
2319 let is_not = matches!(n1, Some(Token::Not));
2320 let is_distinct = matches!(n2, Some(Token::Distinct));
2321 if is_not && is_distinct {
2322 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
2326 } else if matches!(n1, Some(Token::Distinct)) {
2327 self.advance(); self.advance(); }
2330 }
2331 let columns = self.parse_paren_ident_list("UNIQUE")?;
2332 Ok(crate::ast::TableConstraint::Unique {
2333 name: None,
2334 columns,
2335 nulls_not_distinct,
2336 })
2337 }
2338
2339 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2343 self.advance(); if !matches!(self.peek(), Token::LParen) {
2345 return Err(self.err(alloc::format!(
2346 "expected '(' after CHECK, got {:?}",
2347 self.peek()
2348 )));
2349 }
2350 self.advance();
2351 let expr = self.parse_expr(0)?;
2352 if !matches!(self.peek(), Token::RParen) {
2353 return Err(self.err(alloc::format!(
2354 "expected ')' to close CHECK predicate, got {:?}",
2355 self.peek()
2356 )));
2357 }
2358 self.advance();
2359 Ok(crate::ast::TableConstraint::Check { name: None, expr })
2360 }
2361
2362 fn peek_table_level_check_start(&self) -> bool {
2364 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
2365 }
2366
2367 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
2368 if !matches!(self.peek(), Token::LParen) {
2369 return Err(self.err(alloc::format!(
2370 "expected '(' after {ctx}, got {:?}",
2371 self.peek()
2372 )));
2373 }
2374 self.advance();
2375 let mut out = Vec::new();
2376 loop {
2377 out.push(self.expect_ident_like()?);
2378 match self.peek() {
2379 Token::Comma => {
2380 self.advance();
2381 }
2382 Token::RParen => {
2383 self.advance();
2384 break;
2385 }
2386 other => {
2387 return Err(self.err(alloc::format!(
2388 "expected ',' or ')' in {ctx} list, got {other:?}"
2389 )));
2390 }
2391 }
2392 }
2393 if out.is_empty() {
2394 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
2395 }
2396 Ok(out)
2397 }
2398
2399 fn peek_constraint_or_fk_start(&self) -> bool {
2404 let is_constraint_kw = matches!(
2405 self.peek(),
2406 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2407 );
2408 let is_foreign_kw = matches!(
2409 self.peek(),
2410 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
2411 );
2412 is_constraint_kw || is_foreign_kw
2413 }
2414
2415 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
2419 let mut name: Option<String> = None;
2420 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
2421 self.advance();
2422 name = Some(self.expect_ident_like()?);
2423 }
2424 match self.advance() {
2426 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
2427 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
2428 }
2429 match self.advance() {
2431 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
2432 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
2433 }
2434 if !matches!(self.peek(), Token::LParen) {
2436 return Err(self.err(format!(
2437 "expected '(' after FOREIGN KEY, got {:?}",
2438 self.peek()
2439 )));
2440 }
2441 self.advance();
2442 let mut columns = Vec::new();
2443 loop {
2444 columns.push(self.expect_ident_like()?);
2445 match self.peek() {
2446 Token::Comma => {
2447 self.advance();
2448 }
2449 Token::RParen => {
2450 self.advance();
2451 break;
2452 }
2453 other => {
2454 return Err(self.err(format!(
2455 "expected ',' or ')' in FK column list, got {other:?}"
2456 )));
2457 }
2458 }
2459 }
2460 if columns.is_empty() {
2461 return Err(self.err("FOREIGN KEY requires at least one column".into()));
2462 }
2463 let (parent_table, parent_columns, on_delete, on_update) =
2464 self.parse_references_tail(columns.len())?;
2465 Ok(ForeignKeyConstraint {
2466 name,
2467 columns,
2468 parent_table,
2469 parent_columns,
2470 on_delete,
2471 on_update,
2472 })
2473 }
2474
2475 fn parse_references_tail(
2480 &mut self,
2481 expected_arity: usize,
2482 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
2483 match self.advance() {
2484 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
2485 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
2486 }
2487 let parent_table = self.expect_ident_like()?;
2488 let mut parent_columns: Vec<String> = Vec::new();
2489 if matches!(self.peek(), Token::LParen) {
2490 self.advance();
2491 loop {
2492 parent_columns.push(self.expect_ident_like()?);
2493 match self.peek() {
2494 Token::Comma => {
2495 self.advance();
2496 }
2497 Token::RParen => {
2498 self.advance();
2499 break;
2500 }
2501 other => {
2502 return Err(self.err(format!(
2503 "expected ',' or ')' in REFERENCES column list, got {other:?}"
2504 )));
2505 }
2506 }
2507 }
2508 }
2509 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
2510 return Err(self.err(format!(
2511 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
2512 expected_arity,
2513 parent_columns.len()
2514 )));
2515 }
2516 loop {
2522 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2523 return Err(self.err(
2524 "DEFERRABLE constraints are not supported (SPG is single-writer; \
2525 constraints are always evaluated immediately at commit)"
2526 .into(),
2527 ));
2528 }
2529 if matches!(self.peek(), Token::Not) {
2530 let look = self.tokens.get(self.pos + 1);
2531 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2532 self.advance();
2535 self.advance();
2536 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
2538 {
2539 self.advance();
2540 match self.advance() {
2541 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
2542 other => {
2543 return Err(self.err(format!(
2544 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
2545 got {other:?}"
2546 )));
2547 }
2548 }
2549 }
2550 continue;
2551 }
2552 break;
2553 }
2554 break;
2555 }
2556 let mut on_delete = FkAction::Restrict;
2559 let mut on_update = FkAction::Restrict;
2560 let mut seen_on_delete = false;
2561 let mut seen_on_update = false;
2562 loop {
2563 if !matches!(self.peek(), Token::On) {
2564 break;
2565 }
2566 self.advance();
2567 let which = self.advance();
2568 let action = self.parse_fk_action()?;
2569 match which {
2570 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
2571 if seen_on_delete {
2572 return Err(self.err("ON DELETE specified twice".into()));
2573 }
2574 seen_on_delete = true;
2575 on_delete = action;
2576 }
2577 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
2578 if seen_on_update {
2579 return Err(self.err("ON UPDATE specified twice".into()));
2580 }
2581 seen_on_update = true;
2582 on_update = action;
2583 }
2584 other => {
2585 return Err(
2586 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
2587 );
2588 }
2589 }
2590 }
2591 Ok((parent_table, parent_columns, on_delete, on_update))
2592 }
2593
2594 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
2597 match self.advance() {
2598 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
2599 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
2600 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
2601 Token::Null => Ok(FkAction::SetNull),
2602 Token::Default => Ok(FkAction::SetDefault),
2603 other => Err(self.err(format!(
2604 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
2605 ))),
2606 },
2607 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
2608 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
2609 other => Err(self.err(format!(
2610 "expected ACTION after NO in FK action, got {other:?}"
2611 ))),
2612 },
2613 other => Err(self.err(format!(
2614 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
2615 ))),
2616 }
2617 }
2618
2619 fn consume_if_not_exists(&mut self) -> bool {
2622 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2626 if !looks_like_if {
2627 return false;
2628 }
2629 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
2632 return false;
2633 }
2634 if !matches!(
2635 self.tokens.get(self.pos + 2),
2636 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2637 ) {
2638 return false;
2639 }
2640 self.advance(); self.advance(); self.advance(); true
2644 }
2645
2646 fn consume_if_exists(&mut self) -> bool {
2650 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2651 if !looks_like_if {
2652 return false;
2653 }
2654 if !matches!(
2655 self.tokens.get(self.pos + 1),
2656 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2657 ) {
2658 return false;
2659 }
2660 self.advance(); self.advance(); true
2663 }
2664
2665 fn consume_optional_index_column_qualifiers(&mut self) {
2671 loop {
2672 match self.peek() {
2673 Token::Asc | Token::Desc => {
2674 self.advance();
2675 }
2676 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
2677 let look = self.tokens.get(self.pos + 1);
2678 if matches!(
2679 look,
2680 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
2681 || k.eq_ignore_ascii_case("last")
2682 ) {
2683 self.advance();
2684 self.advance();
2685 } else {
2686 break;
2687 }
2688 }
2689 _ => break,
2690 }
2691 }
2692 }
2693
2694 fn parse_create_index_stmt_after_create(
2695 &mut self,
2696 is_unique: bool,
2697 ) -> Result<Statement, ParseError> {
2698 debug_assert!(matches!(self.peek(), Token::Index));
2700 self.advance();
2701 let if_not_exists = self.consume_if_not_exists();
2702 let name = self.expect_ident_like()?;
2703 if !matches!(self.peek(), Token::On) {
2704 return Err(self.err(format!(
2705 "expected ON after CREATE INDEX <name>, got {:?}",
2706 self.peek()
2707 )));
2708 }
2709 self.advance();
2710 let table = self.expect_ident_like()?;
2711 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2716 self.advance();
2717 let m = self.expect_ident_like()?;
2718 match m.to_ascii_lowercase().as_str() {
2719 "hnsw" => IndexMethod::Hnsw,
2720 "btree" => IndexMethod::BTree,
2721 "brin" => IndexMethod::Brin,
2722 "gin" => IndexMethod::Gin,
2727 "gist" | "spgist" | "hash" => IndexMethod::BTree,
2736 "ivfflat" => IndexMethod::Hnsw,
2744 other => {
2745 return Err(self.err(alloc::format!(
2746 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
2747 )));
2748 }
2749 }
2750 } else {
2751 IndexMethod::BTree
2752 };
2753 if !matches!(self.peek(), Token::LParen) {
2754 return Err(self.err(format!(
2755 "expected '(' before indexed column, got {:?}",
2756 self.peek()
2757 )));
2758 }
2759 self.advance();
2760 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
2769 Token::Ident(s) | Token::QuotedIdent(s)
2776 if matches!(
2777 self.tokens.get(self.pos + 1),
2778 Some(Token::RParen | Token::Comma)
2779 ) =>
2780 {
2781 self.advance();
2782 (s, None)
2783 }
2784 Token::Ident(s) | Token::QuotedIdent(s)
2792 if matches!(
2793 self.tokens.get(self.pos + 1),
2794 Some(Token::Ident(op) | Token::QuotedIdent(op))
2795 if is_vector_opclass_name(op)
2796 ) =>
2797 {
2798 self.advance(); self.advance(); (s, None)
2801 }
2802 Token::Ident(_) | Token::QuotedIdent(_) => {
2803 let key_expr = self.parse_expr(0)?;
2804 let primary = extract_first_column(&key_expr).ok_or_else(|| {
2805 self.err("expression index key must reference at least one column".into())
2806 })?;
2807 (primary, Some(key_expr))
2808 }
2809 other => {
2810 return Err(self.err(format!(
2811 "expected column ident or expression, got {other:?}"
2812 )));
2813 }
2814 };
2815 let mut extra_columns: Vec<String> = Vec::new();
2824 self.consume_optional_index_column_qualifiers();
2826 while matches!(self.peek(), Token::Comma) {
2827 self.advance();
2828 let extra = self.expect_ident_like()?;
2829 self.consume_optional_index_column_qualifiers();
2830 extra_columns.push(extra);
2831 }
2832 if !matches!(self.peek(), Token::RParen) {
2833 return Err(self.err(format!(
2834 "expected ')' after indexed column / expression, got {:?}",
2835 self.peek()
2836 )));
2837 }
2838 self.advance();
2839 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
2843 {
2844 self.advance();
2845 if !matches!(self.peek(), Token::LParen) {
2846 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
2847 }
2848 self.advance();
2849 let mut cols = Vec::new();
2850 loop {
2851 cols.push(self.expect_ident_like()?);
2852 match self.peek() {
2853 Token::Comma => {
2854 self.advance();
2855 }
2856 Token::RParen => {
2857 self.advance();
2858 break;
2859 }
2860 other => {
2861 return Err(self.err(format!(
2862 "expected ',' or ')' in INCLUDE list, got {other:?}"
2863 )));
2864 }
2865 }
2866 }
2867 cols
2868 } else {
2869 Vec::new()
2870 };
2871 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2877 self.advance();
2878 if !matches!(self.peek(), Token::LParen) {
2879 return Err(self.err(format!(
2880 "expected '(' after WITH in CREATE INDEX, got {:?}",
2881 self.peek()
2882 )));
2883 }
2884 self.advance();
2885 loop {
2886 if matches!(self.peek(), Token::RParen) {
2887 self.advance();
2888 break;
2889 }
2890 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
2893 self.advance();
2894 let _ = self.advance(); }
2896 match self.peek() {
2897 Token::Comma => {
2898 self.advance();
2899 }
2900 Token::RParen => {
2901 self.advance();
2902 break;
2903 }
2904 other => {
2905 return Err(self.err(format!(
2906 "expected ',' or ')' in WITH (…) clause, got {other:?}"
2907 )));
2908 }
2909 }
2910 }
2911 }
2912 let partial_predicate = if matches!(self.peek(), Token::Where) {
2914 self.advance();
2915 Some(self.parse_expr(0)?)
2916 } else {
2917 None
2918 };
2919 if is_unique && !matches!(method, IndexMethod::BTree) {
2924 return Err(self.err(alloc::format!(
2925 "UNIQUE is only supported on BTree indexes, got USING {:?}",
2926 method
2927 )));
2928 }
2929 Ok(Statement::CreateIndex(CreateIndexStatement {
2930 name,
2931 table,
2932 column,
2933 method,
2934 if_not_exists,
2935 included_columns,
2936 partial_predicate,
2937 extra_columns: extra_columns.clone(),
2938 expression,
2939 is_unique,
2940 }))
2941 }
2942
2943 fn parse_column_def_with_fk(
2948 &mut self,
2949 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
2950 let col = self.parse_column_def()?;
2951 let inline_references = matches!(
2953 self.peek(),
2954 Token::Ident(s) if s.eq_ignore_ascii_case("references")
2955 );
2956 if !inline_references {
2957 return Ok((col, None));
2958 }
2959 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
2960 let fk = ForeignKeyConstraint {
2961 name: None,
2962 columns: vec![col.name.clone()],
2963 parent_table,
2964 parent_columns,
2965 on_delete,
2966 on_update,
2967 };
2968 Ok((col, Some(fk)))
2969 }
2970
2971 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
2979 let (ty, _, _) = self.parse_type_with_implied_flags()?;
2980 Ok(ty)
2981 }
2982
2983 fn parse_type_with_implied_flags(
2984 &mut self,
2985 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
2986 let ty_ident = match self.advance() {
2987 Token::Ident(s) => s,
2988 other => {
2989 return Err(ParseError {
2990 message: format!("expected column type, got {other:?}"),
2991 token_pos: self.pos.saturating_sub(1),
2992 });
2993 }
2994 };
2995 let mut implied_auto_increment = false;
2996 let mut implied_not_null = false;
2997 let mut ty = match ty_ident.as_str() {
2998 "smallserial" | "serial2" => {
3000 implied_auto_increment = true;
3001 implied_not_null = true;
3002 ColumnTypeName::SmallInt
3003 }
3004 "serial" | "serial4" => {
3005 implied_auto_increment = true;
3006 implied_not_null = true;
3007 ColumnTypeName::Int
3008 }
3009 "bigserial" | "serial8" => {
3010 implied_auto_increment = true;
3011 implied_not_null = true;
3012 ColumnTypeName::BigInt
3013 }
3014 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
3020 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
3022 "bigint" => ColumnTypeName::BigInt,
3023 "float" | "double" | "real" => {
3028 if ty_ident.eq_ignore_ascii_case("double")
3029 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
3030 {
3031 self.advance();
3032 }
3033 ColumnTypeName::Float
3034 }
3035 "float4" | "float8" => ColumnTypeName::Float,
3037 "text" => ColumnTypeName::Text,
3038 "bool" | "boolean" => ColumnTypeName::Bool,
3039 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
3040 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
3041 "vector" => {
3042 let dim = self.parse_paren_size("VECTOR")?;
3043 let encoding = self.parse_optional_vector_encoding()?;
3044 ColumnTypeName::Vector { dim, encoding }
3045 }
3046 "numeric" => {
3047 let (precision, scale) = self.parse_optional_numeric_params()?;
3048 ColumnTypeName::Numeric(precision, scale)
3049 }
3050 "date" => ColumnTypeName::Date,
3051 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
3054 "timestamptz" => ColumnTypeName::Timestamptz,
3058 "json" => ColumnTypeName::Json,
3063 "jsonb" => ColumnTypeName::Jsonb,
3064 "bytea" | "bytes" => ColumnTypeName::Bytes,
3070 "tsvector" => ColumnTypeName::TsVector,
3075 "tsquery" => ColumnTypeName::TsQuery,
3076 other => {
3077 return Err(ParseError {
3078 message: format!("unsupported column type {other:?}"),
3079 token_pos: self.pos.saturating_sub(1),
3080 });
3081 }
3082 };
3083 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
3088 self.advance();
3089 }
3090 if matches!(self.peek(), Token::LBracket) {
3095 self.advance();
3096 if !matches!(self.peek(), Token::RBracket) {
3097 return Err(self.err(alloc::format!(
3098 "TEXT[] takes no dimension; got {:?}",
3099 self.peek()
3100 )));
3101 }
3102 self.advance();
3103 ty = match ty {
3107 ColumnTypeName::Text => ColumnTypeName::TextArray,
3108 ColumnTypeName::Int => ColumnTypeName::IntArray,
3109 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
3110 other => {
3111 return Err(self.err(alloc::format!(
3112 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
3113 )));
3114 }
3115 };
3116 }
3117 Ok((ty, implied_auto_increment, implied_not_null))
3118 }
3119
3120 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
3121 let name = self.expect_ident_like()?;
3122 let (ty, implied_auto_increment, implied_not_null) =
3123 self.parse_type_with_implied_flags()?;
3124 let mut default: Option<Expr> = None;
3128 let mut nullable = !implied_not_null;
3129 let mut nullability_seen = implied_not_null;
3130 let mut auto_increment = implied_auto_increment;
3131 let mut is_primary_key = false;
3132 let mut is_unique = false;
3133 let mut check: Option<Expr> = None;
3134 loop {
3135 if matches!(self.peek(), Token::Default) {
3136 if default.is_some() {
3137 return Err(self.err("DEFAULT specified twice".into()));
3138 }
3139 self.advance();
3140 default = Some(self.parse_expr(0)?);
3141 continue;
3142 }
3143 if matches!(self.peek(), Token::Not) {
3144 if nullability_seen {
3145 return Err(self.err("NOT NULL specified twice".into()));
3146 }
3147 self.advance();
3148 if !matches!(self.peek(), Token::Null) {
3149 return Err(self.err(format!(
3150 "expected NULL after NOT in column def, got {:?}",
3151 self.peek()
3152 )));
3153 }
3154 self.advance();
3155 nullable = false;
3156 nullability_seen = true;
3157 continue;
3158 }
3159 if let Token::Ident(s) = self.peek()
3162 && (s.eq_ignore_ascii_case("auto_increment")
3163 || s.eq_ignore_ascii_case("autoincrement"))
3164 {
3165 if auto_increment {
3166 return Err(self.err("AUTO_INCREMENT specified twice".into()));
3167 }
3168 self.advance();
3169 auto_increment = true;
3170 continue;
3171 }
3172 if let Token::Ident(s) = self.peek()
3177 && s.eq_ignore_ascii_case("primary")
3178 {
3179 if is_primary_key {
3180 return Err(self.err("PRIMARY KEY specified twice".into()));
3181 }
3182 let next = self.tokens.get(self.pos + 1);
3184 let next_is_key = matches!(
3185 next,
3186 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
3187 );
3188 if !next_is_key {
3189 return Err(self.err(format!(
3190 "expected KEY after PRIMARY in column def, got {:?}",
3191 next
3192 )));
3193 }
3194 self.advance(); self.advance(); is_primary_key = true;
3197 if nullability_seen && nullable {
3198 return Err(self.err(
3199 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
3200 ));
3201 }
3202 nullable = false;
3203 nullability_seen = true;
3204 continue;
3205 }
3206 if let Token::Ident(s) = self.peek()
3210 && s.eq_ignore_ascii_case("unique")
3211 {
3212 if is_unique {
3213 return Err(self.err("UNIQUE specified twice".into()));
3214 }
3215 self.advance();
3216 is_unique = true;
3217 continue;
3218 }
3219 if let Token::Ident(s) = self.peek()
3224 && s.eq_ignore_ascii_case("check")
3225 {
3226 self.advance();
3227 if !matches!(self.peek(), Token::LParen) {
3228 return Err(self.err(alloc::format!(
3229 "expected '(' after CHECK in column def, got {:?}",
3230 self.peek()
3231 )));
3232 }
3233 self.advance();
3234 let pred = self.parse_expr(0)?;
3235 if !matches!(self.peek(), Token::RParen) {
3236 return Err(self.err(alloc::format!(
3237 "expected ')' to close CHECK predicate, got {:?}",
3238 self.peek()
3239 )));
3240 }
3241 self.advance();
3242 check = Some(match check.take() {
3243 Some(prev) => Expr::Binary {
3244 op: BinOp::And,
3245 lhs: Box::new(prev),
3246 rhs: Box::new(pred),
3247 },
3248 None => pred,
3249 });
3250 continue;
3251 }
3252 break;
3253 }
3254 Ok(ColumnDef {
3255 name,
3256 ty,
3257 nullable,
3258 default,
3259 auto_increment,
3260 is_primary_key,
3261 is_unique,
3262 check,
3263 })
3264 }
3265
3266 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
3270 if !matches!(self.peek(), Token::LParen) {
3271 return Ok((0, 0));
3275 }
3276 self.advance();
3277 let precision = match self.advance() {
3278 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
3279 other => {
3280 return Err(ParseError {
3281 message: format!(
3282 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
3283 ),
3284 token_pos: self.pos.saturating_sub(1),
3285 });
3286 }
3287 };
3288 let scale = if matches!(self.peek(), Token::Comma) {
3289 self.advance();
3290 match self.advance() {
3291 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
3292 u8::try_from(n).expect("range-checked")
3293 }
3294 other => {
3295 return Err(ParseError {
3296 message: format!(
3297 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
3298 ),
3299 token_pos: self.pos.saturating_sub(1),
3300 });
3301 }
3302 }
3303 } else {
3304 0
3305 };
3306 if !matches!(self.peek(), Token::RParen) {
3307 return Err(self.err(format!(
3308 "expected ')' to close NUMERIC params, got {:?}",
3309 self.peek()
3310 )));
3311 }
3312 self.advance();
3313 Ok((precision, scale))
3314 }
3315
3316 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
3324 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3325 return Ok(VecEncoding::F32);
3326 }
3327 let n1 = self.tokens.get(self.pos + 1);
3333 let next_is_encoding = matches!(
3334 n1,
3335 Some(Token::Ident(s))
3336 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
3337 );
3338 if !next_is_encoding {
3339 return Ok(VecEncoding::F32);
3340 }
3341 self.advance();
3342 let enc_ident = match self.advance() {
3343 Token::Ident(s) => s,
3344 other => {
3345 return Err(self.err(format!(
3346 "expected vector encoding after USING, got {other:?}"
3347 )));
3348 }
3349 };
3350 match enc_ident.to_ascii_lowercase().as_str() {
3351 "sq8" => Ok(VecEncoding::Sq8),
3352 "half" => Ok(VecEncoding::F16),
3355 other => Err(self.err(format!(
3356 "unknown vector encoding {other:?}; supported: SQ8, HALF"
3357 ))),
3358 }
3359 }
3360
3361 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
3362 if !matches!(self.peek(), Token::LParen) {
3363 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
3364 }
3365 self.advance();
3366 let n = match self.advance() {
3367 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
3368 message: format!("{label} size too large: {n}"),
3369 token_pos: self.pos.saturating_sub(1),
3370 })?,
3371 other => {
3372 return Err(ParseError {
3373 message: format!("expected positive integer {label} size, got {other:?}"),
3374 token_pos: self.pos.saturating_sub(1),
3375 });
3376 }
3377 };
3378 if !matches!(self.peek(), Token::RParen) {
3379 return Err(self.err(format!(
3380 "expected ')' after {label} size, got {:?}",
3381 self.peek()
3382 )));
3383 }
3384 self.advance();
3385 Ok(n)
3386 }
3387
3388 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
3389 debug_assert!(matches!(self.peek(), Token::Insert));
3390 self.advance();
3391 if !matches!(self.peek(), Token::Into) {
3392 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
3393 }
3394 self.advance();
3395 let table = self.expect_ident_like()?;
3396 let columns = if matches!(self.peek(), Token::LParen) {
3398 self.advance();
3399 let mut names = Vec::new();
3400 loop {
3401 names.push(self.expect_ident_like()?);
3402 match self.peek() {
3403 Token::Comma => {
3404 self.advance();
3405 }
3406 Token::RParen => {
3407 self.advance();
3408 break;
3409 }
3410 other => {
3411 return Err(self.err(format!(
3412 "expected ',' or ')' in INSERT column list, got {other:?}"
3413 )));
3414 }
3415 }
3416 }
3417 Some(names)
3418 } else {
3419 None
3420 };
3421 if matches!(self.peek(), Token::Select) {
3424 let select_stmt = match self.parse_select_stmt()? {
3425 Statement::Select(s) => s,
3426 other => {
3427 return Err(self.err(alloc::format!(
3428 "expected SELECT after INSERT INTO ... target, got {other:?}"
3429 )));
3430 }
3431 };
3432 let on_conflict = self.parse_optional_on_conflict()?;
3433 let returning = self.parse_optional_returning()?;
3434 return Ok(Statement::Insert(InsertStatement {
3435 table,
3436 columns,
3437 rows: Vec::new(),
3438 select_source: Some(Box::new(select_stmt)),
3439 on_conflict,
3440 returning,
3441 }));
3442 }
3443 if !matches!(self.peek(), Token::Values) {
3444 return Err(self.err(format!(
3445 "expected VALUES or SELECT after table name, got {:?}",
3446 self.peek()
3447 )));
3448 }
3449 self.advance();
3450 if !matches!(self.peek(), Token::LParen) {
3451 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
3452 }
3453 let mut rows = Vec::new();
3454 loop {
3455 if !matches!(self.peek(), Token::LParen) {
3457 return Err(self.err(format!(
3458 "expected '(' for next VALUES tuple, got {:?}",
3459 self.peek()
3460 )));
3461 }
3462 self.advance();
3463 let mut tuple = Vec::new();
3464 loop {
3465 tuple.push(self.parse_expr(0)?);
3466 match self.peek() {
3467 Token::Comma => {
3468 self.advance();
3469 }
3470 Token::RParen => {
3471 self.advance();
3472 break;
3473 }
3474 other => {
3475 return Err(self.err(format!(
3476 "expected ',' or ')' in VALUES tuple, got {other:?}"
3477 )));
3478 }
3479 }
3480 }
3481 if tuple.is_empty() {
3482 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
3483 }
3484 rows.push(tuple);
3485 if matches!(self.peek(), Token::Comma) {
3487 self.advance();
3488 } else {
3489 break;
3490 }
3491 }
3492 let on_conflict = self.parse_optional_on_conflict()?;
3493 let returning = self.parse_optional_returning()?;
3494 Ok(Statement::Insert(InsertStatement {
3495 table,
3496 columns,
3497 rows,
3498 select_source: None,
3499 on_conflict,
3500 returning,
3501 }))
3502 }
3503
3504 fn parse_optional_on_conflict(
3509 &mut self,
3510 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
3511 if !matches!(self.peek(), Token::On) {
3512 return Ok(None);
3513 }
3514 let next_is_conflict = matches!(
3517 self.tokens.get(self.pos + 1),
3518 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
3519 );
3520 if !next_is_conflict {
3521 return Ok(None);
3522 }
3523 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
3527 if matches!(self.peek(), Token::LParen) {
3528 self.advance();
3529 loop {
3530 target_columns.push(self.expect_ident_like()?);
3531 match self.peek() {
3532 Token::Comma => {
3533 self.advance();
3534 }
3535 Token::RParen => {
3536 self.advance();
3537 break;
3538 }
3539 other => {
3540 return Err(self.err(alloc::format!(
3541 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
3542 )));
3543 }
3544 }
3545 }
3546 }
3547 match self.advance() {
3549 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
3550 other => {
3551 return Err(self.err(alloc::format!(
3552 "expected DO after ON CONFLICT [(…)], got {other:?}"
3553 )));
3554 }
3555 }
3556 let action = match self.advance() {
3558 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
3559 crate::ast::OnConflictAction::Nothing
3560 }
3561 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3562 self.parse_on_conflict_update_action()?
3563 }
3564 other => {
3565 return Err(self.err(alloc::format!(
3566 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
3567 )));
3568 }
3569 };
3570 Ok(Some(crate::ast::OnConflictClause {
3571 target_columns,
3572 action,
3573 }))
3574 }
3575
3576 fn parse_on_conflict_update_action(
3580 &mut self,
3581 ) -> Result<crate::ast::OnConflictAction, ParseError> {
3582 match self.advance() {
3584 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
3585 other => {
3586 return Err(self.err(alloc::format!(
3587 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
3588 )));
3589 }
3590 }
3591 let mut assignments: Vec<(String, Expr)> = Vec::new();
3592 loop {
3593 let col = self.expect_ident_like()?;
3594 if !matches!(self.peek(), Token::Eq) {
3595 return Err(self.err(alloc::format!(
3596 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
3597 self.peek()
3598 )));
3599 }
3600 self.advance();
3601 let value = self.parse_expr(0)?;
3602 assignments.push((col, value));
3603 if matches!(self.peek(), Token::Comma) {
3604 self.advance();
3605 continue;
3606 }
3607 break;
3608 }
3609 let where_ = if matches!(self.peek(), Token::Where) {
3610 self.advance();
3611 Some(self.parse_expr(0)?)
3612 } else {
3613 None
3614 };
3615 Ok(crate::ast::OnConflictAction::Update {
3616 assignments,
3617 where_,
3618 })
3619 }
3620
3621 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
3622 let mut items = Vec::new();
3623 loop {
3624 items.push(self.parse_select_item()?);
3625 if matches!(self.peek(), Token::Comma) {
3626 self.advance();
3627 } else {
3628 break;
3629 }
3630 }
3631 Ok(items)
3632 }
3633
3634 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
3635 if matches!(self.peek(), Token::Star) {
3636 self.advance();
3637 return Ok(SelectItem::Wildcard);
3638 }
3639 let expr = self.parse_expr(0)?;
3640 let alias = self.parse_optional_alias();
3641 Ok(SelectItem::Expr { expr, alias })
3642 }
3643
3644 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
3645 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
3649 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
3650 {
3651 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
3654 if !matches!(self.peek(), Token::RParen) {
3655 return Err(self.err(alloc::format!(
3656 "expected ')' after unnest() argument, got {:?}",
3657 self.peek()
3658 )));
3659 }
3660 self.advance();
3661 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
3662 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
3663 return Ok(TableRef {
3664 name,
3665 alias: alias_ident,
3666 as_of_segment: None,
3667 unnest_expr: Some(Box::new(expr)),
3668 unnest_column_aliases,
3669 });
3670 }
3671 let name = self.expect_ident_like()?;
3672 let as_of_segment = if matches!(self.peek(), Token::As)
3678 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
3679 {
3680 self.advance(); self.advance(); let kw = match self.peek().clone() {
3683 Token::Ident(s) | Token::QuotedIdent(s) => s,
3684 other => {
3685 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
3686 }
3687 };
3688 if !kw.eq_ignore_ascii_case("segment") {
3689 return Err(self.err(format!(
3690 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
3691 )));
3692 }
3693 self.advance();
3694 let id = match self.advance() {
3697 Token::String(s) => s
3698 .parse::<u32>()
3699 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3700 Token::Integer(n) => u32::try_from(n)
3701 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3702 other => {
3703 return Err(self.err(format!(
3704 "expected segment id literal after AS OF SEGMENT, got {other:?}"
3705 )));
3706 }
3707 };
3708 Some(id)
3709 } else {
3710 None
3711 };
3712 let alias = self.parse_optional_alias();
3713 Ok(TableRef {
3714 name,
3715 alias,
3716 as_of_segment,
3717 unnest_expr: None,
3718 unnest_column_aliases: Vec::new(),
3719 })
3720 }
3721
3722 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
3728 let alias = self.parse_optional_alias();
3729 if alias.is_none() {
3730 return (None, Vec::new());
3731 }
3732 let mut cols: Vec<String> = Vec::new();
3733 if matches!(self.peek(), Token::LParen) {
3734 self.advance();
3735 loop {
3736 match self.peek().clone() {
3737 Token::Ident(s) | Token::QuotedIdent(s) => {
3738 self.advance();
3739 cols.push(s);
3740 }
3741 _ => break,
3742 }
3743 if matches!(self.peek(), Token::Comma) {
3744 self.advance();
3745 continue;
3746 }
3747 break;
3748 }
3749 if matches!(self.peek(), Token::RParen) {
3750 self.advance();
3751 }
3752 }
3753 (alias, cols)
3754 }
3755
3756 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
3761 let primary = self.parse_table_ref()?;
3762 let mut joins = Vec::new();
3763 loop {
3764 if matches!(self.peek(), Token::Comma) {
3766 self.advance();
3767 let table = self.parse_table_ref()?;
3768 joins.push(FromJoin {
3769 kind: JoinKind::Cross,
3770 table,
3771 on: None,
3772 });
3773 continue;
3774 }
3775 let kind =
3778 match self.peek() {
3779 Token::Inner => {
3780 self.advance();
3781 if !matches!(self.peek(), Token::Join) {
3782 return Err(self
3783 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
3784 }
3785 self.advance();
3786 JoinKind::Inner
3787 }
3788 Token::Left => {
3789 self.advance();
3790 if matches!(self.peek(), Token::Outer) {
3791 self.advance();
3792 }
3793 if !matches!(self.peek(), Token::Join) {
3794 return Err(self.err(format!(
3795 "expected JOIN after LEFT [OUTER], got {:?}",
3796 self.peek()
3797 )));
3798 }
3799 self.advance();
3800 JoinKind::Left
3801 }
3802 Token::Cross => {
3803 self.advance();
3804 if !matches!(self.peek(), Token::Join) {
3805 return Err(self
3806 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
3807 }
3808 self.advance();
3809 JoinKind::Cross
3810 }
3811 Token::Join => {
3812 self.advance();
3813 JoinKind::Inner
3814 }
3815 _ => break,
3816 };
3817 let table = self.parse_table_ref()?;
3818 let on = if matches!(self.peek(), Token::On) {
3819 self.advance();
3820 Some(self.parse_expr(0)?)
3821 } else if kind == JoinKind::Cross {
3822 None
3823 } else {
3824 return Err(self.err(format!(
3825 "expected ON after {:?} JOIN, got {:?}",
3826 kind,
3827 self.peek()
3828 )));
3829 };
3830 joins.push(FromJoin { kind, table, on });
3831 }
3832 Ok(FromClause { primary, joins })
3833 }
3834
3835 fn parse_optional_alias(&mut self) -> Option<String> {
3840 if matches!(self.peek(), Token::As) {
3841 self.advance();
3842 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3847 return self.expect_ident_like().ok();
3848 }
3849 return None;
3850 }
3851 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3852 return self.expect_ident_like().ok();
3853 }
3854 None
3855 }
3856
3857 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
3859 let mut lhs = self.parse_unary()?;
3860 while let Some((op, prec)) = binop_from(self.peek()) {
3861 if prec < min_prec {
3862 break;
3863 }
3864 self.advance();
3865 let any_kind = match self.peek() {
3870 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
3871 Some(false)
3872 }
3873 Token::Ident(s) | Token::QuotedIdent(s)
3874 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
3875 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
3876 {
3877 Some(s.eq_ignore_ascii_case("any"))
3878 }
3879 _ => None,
3880 };
3881 if let Some(is_any) = any_kind {
3882 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
3885 if !matches!(self.peek(), Token::RParen) {
3886 return Err(self.err(alloc::format!(
3887 "expected ')' after ANY/ALL argument, got {:?}",
3888 self.peek()
3889 )));
3890 }
3891 self.advance();
3892 lhs = Expr::AnyAll {
3893 expr: Box::new(lhs),
3894 op,
3895 array: Box::new(arr),
3896 is_any,
3897 };
3898 continue;
3899 }
3900 let rhs = self.parse_expr(prec + 1)?;
3901 lhs = Expr::Binary {
3902 lhs: Box::new(lhs),
3903 op,
3904 rhs: Box::new(rhs),
3905 };
3906 }
3907 Ok(lhs)
3908 }
3909
3910 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
3911 match self.peek() {
3912 Token::Not => {
3913 self.advance();
3914 let e = self.parse_expr(3)?;
3917 Ok(Expr::Unary {
3918 op: UnOp::Not,
3919 expr: Box::new(e),
3920 })
3921 }
3922 Token::Minus => {
3923 self.advance();
3924 let e = self.parse_expr(8)?;
3927 Ok(Expr::Unary {
3928 op: UnOp::Neg,
3929 expr: Box::new(e),
3930 })
3931 }
3932 _ => self.parse_atom(),
3933 }
3934 }
3935
3936 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
3937 let tok_pos = self.pos;
3938 match self.advance() {
3939 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
3940 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
3941 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
3942 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
3943 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
3944 Token::Null => Ok(Expr::Literal(Literal::Null)),
3945 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
3949 Token::LParen => {
3950 if matches!(self.peek(), Token::Select) {
3954 let inner = self.parse_select_stmt()?;
3955 match self.advance() {
3956 Token::RParen => {
3957 let Statement::Select(s) = inner else {
3958 unreachable!("parse_select_stmt returns Select")
3959 };
3960 Ok(Expr::ScalarSubquery(Box::new(s)))
3961 }
3962 other => Err(ParseError {
3963 message: format!("expected ')' after scalar subquery, got {other:?}"),
3964 token_pos: self.pos.saturating_sub(1),
3965 }),
3966 }
3967 } else {
3968 let e = self.parse_expr(0)?;
3969 match self.advance() {
3970 Token::RParen => Ok(e),
3971 other => Err(ParseError {
3972 message: format!("expected ')', got {other:?}"),
3973 token_pos: self.pos.saturating_sub(1),
3974 }),
3975 }
3976 }
3977 }
3978 Token::LBracket => self.parse_vector_literal_body(),
3979 Token::Extract => self.parse_extract_atom(),
3980 Token::Interval => self.parse_interval_atom(),
3981 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
3986 self.parse_exists_atom(false)
3987 }
3988 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
3992 self.parse_case_atom()
3993 }
3994 Token::Ident(s) | Token::QuotedIdent(s)
3998 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
3999 {
4000 self.advance(); let mut items: Vec<Expr> = Vec::new();
4002 if !matches!(self.peek(), Token::RBracket) {
4003 loop {
4004 items.push(self.parse_expr(0)?);
4005 match self.peek() {
4006 Token::Comma => {
4007 self.advance();
4008 }
4009 Token::RBracket => break,
4010 other => {
4011 return Err(self.err(alloc::format!(
4012 "expected ',' or ']' in ARRAY literal, got {other:?}"
4013 )));
4014 }
4015 }
4016 }
4017 }
4018 self.advance(); Ok(Expr::Array(items))
4020 }
4021 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
4022 other => Err(ParseError {
4023 message: format!("unexpected token {other:?} in expression"),
4024 token_pos: tok_pos,
4025 }),
4026 }
4027 .and_then(|atom| self.finish_postfix_casts(atom))
4029 }
4030
4031 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
4034 loop {
4035 if matches!(self.peek(), Token::DoubleColon) {
4036 self.advance();
4037 let target = match self.advance() {
4042 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
4043 "int" | "integer" | "int4" => {
4044 if matches!(self.peek(), Token::LBracket)
4045 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4046 {
4047 self.advance();
4048 self.advance();
4049 CastTarget::IntArray
4050 } else {
4051 CastTarget::Int
4052 }
4053 }
4054 "bigint" | "int8" => {
4055 if matches!(self.peek(), Token::LBracket)
4056 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4057 {
4058 self.advance();
4059 self.advance();
4060 CastTarget::BigIntArray
4061 } else {
4062 CastTarget::BigInt
4063 }
4064 }
4065 "float" | "double" | "real" => CastTarget::Float,
4066 "text" => {
4067 if matches!(self.peek(), Token::LBracket)
4069 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4070 {
4071 self.advance();
4072 self.advance();
4073 CastTarget::TextArray
4074 } else {
4075 CastTarget::Text
4076 }
4077 }
4078 "bool" | "boolean" => CastTarget::Bool,
4079 "vector" => CastTarget::Vector,
4080 "date" => CastTarget::Date,
4081 "timestamp" | "datetime" => CastTarget::Timestamp,
4082 "timestamptz" => CastTarget::Timestamptz,
4083 "interval" => CastTarget::Interval,
4084 "json" => CastTarget::Json,
4085 "jsonb" => CastTarget::Jsonb,
4086 "regtype" => CastTarget::RegType,
4087 "regclass" => CastTarget::RegClass,
4088 "tsvector" => CastTarget::TsVector,
4092 "tsquery" => CastTarget::TsQuery,
4093 other => {
4094 return Err(ParseError {
4095 message: format!("unsupported cast target `::{other}`"),
4096 token_pos: self.pos.saturating_sub(1),
4097 });
4098 }
4099 },
4100 Token::Interval => CastTarget::Interval,
4101 other => {
4102 return Err(ParseError {
4103 message: format!("expected type ident after `::`, got {other:?}"),
4104 token_pos: self.pos.saturating_sub(1),
4105 });
4106 }
4107 };
4108 expr = Expr::Cast {
4109 expr: Box::new(expr),
4110 target,
4111 };
4112 continue;
4113 }
4114 if matches!(self.peek(), Token::Is) {
4115 self.advance();
4116 let negated = if matches!(self.peek(), Token::Not) {
4117 self.advance();
4118 true
4119 } else {
4120 false
4121 };
4122 if matches!(self.peek(), Token::Distinct) {
4125 self.advance();
4126 if !matches!(self.peek(), Token::From) {
4127 return Err(self.err(format!(
4128 "expected FROM after IS{} DISTINCT, got {:?}",
4129 if negated { " NOT" } else { "" },
4130 self.peek()
4131 )));
4132 }
4133 self.advance();
4134 let rhs = self.parse_expr(20)?;
4138 let op = if negated {
4139 BinOp::IsNotDistinctFrom
4140 } else {
4141 BinOp::IsDistinctFrom
4142 };
4143 expr = Expr::Binary {
4144 op,
4145 lhs: Box::new(expr),
4146 rhs: Box::new(rhs),
4147 };
4148 continue;
4149 }
4150 if !matches!(self.peek(), Token::Null) {
4151 return Err(self.err(format!(
4152 "expected NULL or DISTINCT after IS{}, got {:?}",
4153 if negated { " NOT" } else { "" },
4154 self.peek()
4155 )));
4156 }
4157 self.advance();
4158 expr = Expr::IsNull {
4159 expr: Box::new(expr),
4160 negated,
4161 };
4162 continue;
4163 }
4164 let negated = if matches!(self.peek(), Token::Not) {
4168 let next = self.tokens.get(self.pos + 1);
4169 matches!(next, Some(Token::Between | Token::In | Token::Like))
4170 } else {
4171 false
4172 };
4173 if negated {
4174 self.advance();
4175 }
4176 if matches!(self.peek(), Token::Between) {
4177 expr = self.parse_between_tail(expr, negated)?;
4178 continue;
4179 }
4180 if matches!(self.peek(), Token::In) {
4181 expr = self.parse_in_tail(expr, negated)?;
4182 continue;
4183 }
4184 if matches!(self.peek(), Token::Like) {
4185 self.advance();
4186 let pattern = self.parse_expr(5)?;
4189 expr = Expr::Like {
4190 expr: Box::new(expr),
4191 pattern: Box::new(pattern),
4192 negated,
4193 };
4194 continue;
4195 }
4196 if matches!(self.peek(), Token::LBracket) {
4200 self.advance();
4201 let index = self.parse_expr(0)?;
4202 if !matches!(self.peek(), Token::RBracket) {
4203 return Err(self.err(alloc::format!(
4204 "expected ']' after array index, got {:?}",
4205 self.peek()
4206 )));
4207 }
4208 self.advance();
4209 expr = Expr::ArraySubscript {
4210 target: Box::new(expr),
4211 index: Box::new(index),
4212 };
4213 continue;
4214 }
4215 return Ok(expr);
4216 }
4217 }
4218
4219 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4223 self.advance(); let low = self.parse_expr(5)?;
4225 if !matches!(self.peek(), Token::And) {
4226 return Err(self.err(format!(
4227 "expected AND after BETWEEN low bound, got {:?}",
4228 self.peek()
4229 )));
4230 }
4231 self.advance();
4232 let high = self.parse_expr(5)?;
4233 let target = Box::new(expr);
4234 let combined = Expr::Binary {
4235 lhs: Box::new(Expr::Binary {
4236 lhs: target.clone(),
4237 op: BinOp::GtEq,
4238 rhs: Box::new(low),
4239 }),
4240 op: BinOp::And,
4241 rhs: Box::new(Expr::Binary {
4242 lhs: target,
4243 op: BinOp::LtEq,
4244 rhs: Box::new(high),
4245 }),
4246 };
4247 Ok(maybe_not(combined, negated))
4248 }
4249
4250 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
4255 let mut recursive = false;
4260 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4261 && s.eq_ignore_ascii_case("recursive")
4262 {
4263 self.advance();
4264 recursive = true;
4265 }
4266 let mut ctes = Vec::new();
4267 loop {
4268 let name = self.expect_ident_like()?;
4269 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
4273 self.advance();
4274 let mut names = Vec::new();
4275 loop {
4276 names.push(self.expect_ident_like()?);
4277 if matches!(self.peek(), Token::Comma) {
4278 self.advance();
4279 continue;
4280 }
4281 break;
4282 }
4283 if !matches!(self.peek(), Token::RParen) {
4284 return Err(self.err(format!(
4285 "expected ')' to close CTE column list, got {:?}",
4286 self.peek()
4287 )));
4288 }
4289 self.advance();
4290 names
4291 } else {
4292 Vec::new()
4293 };
4294 if !matches!(self.peek(), Token::As) {
4298 return Err(self.err(format!(
4299 "expected AS after CTE name {name:?}, got {:?}",
4300 self.peek()
4301 )));
4302 }
4303 self.advance();
4304 if !matches!(self.peek(), Token::LParen) {
4305 return Err(self.err(format!(
4306 "expected '(' after AS in WITH clause, got {:?}",
4307 self.peek()
4308 )));
4309 }
4310 self.advance();
4311 if !matches!(self.peek(), Token::Select) {
4312 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
4313 }
4314 let inner = self.parse_select_stmt()?;
4315 if !matches!(self.peek(), Token::RParen) {
4316 return Err(self.err(format!(
4317 "expected ')' after CTE body, got {:?}",
4318 self.peek()
4319 )));
4320 }
4321 self.advance();
4322 let Statement::Select(body) = inner else {
4323 unreachable!("parse_select_stmt returns Select")
4324 };
4325 ctes.push(crate::ast::Cte {
4326 name,
4327 body,
4328 recursive,
4329 column_overrides,
4330 });
4331 if matches!(self.peek(), Token::Comma) {
4332 self.advance();
4333 continue;
4334 }
4335 break;
4336 }
4337 if !matches!(self.peek(), Token::Select) {
4339 return Err(self.err(format!(
4340 "expected SELECT after WITH clause, got {:?}",
4341 self.peek()
4342 )));
4343 }
4344 let body_stmt = self.parse_select_stmt()?;
4345 let Statement::Select(mut body) = body_stmt else {
4346 unreachable!()
4347 };
4348 body.ctes = ctes;
4349 Ok(Statement::Select(body))
4350 }
4351
4352 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
4361 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
4365 None
4366 } else {
4367 Some(Box::new(self.parse_expr(0)?))
4368 };
4369 let mut branches: Vec<(Expr, Expr)> = Vec::new();
4370 loop {
4371 match self.peek() {
4372 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
4373 self.advance();
4374 let cond = self.parse_expr(0)?;
4375 match self.peek() {
4376 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
4377 self.advance();
4378 }
4379 other => {
4380 return Err(self.err(alloc::format!(
4381 "expected THEN after CASE WHEN <expr>, got {other:?}"
4382 )));
4383 }
4384 }
4385 let value = self.parse_expr(0)?;
4386 branches.push((cond, value));
4387 }
4388 _ => break,
4389 }
4390 }
4391 if branches.is_empty() {
4392 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
4393 }
4394 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
4395 {
4396 self.advance();
4397 Some(Box::new(self.parse_expr(0)?))
4398 } else {
4399 None
4400 };
4401 match self.peek() {
4402 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
4403 self.advance();
4404 }
4405 other => {
4406 return Err(self.err(alloc::format!(
4407 "expected END to close CASE expression, got {other:?}"
4408 )));
4409 }
4410 }
4411 Ok(Expr::Case {
4412 operand,
4413 branches,
4414 else_branch,
4415 })
4416 }
4417
4418 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
4419 if !matches!(self.peek(), Token::LParen) {
4420 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
4421 }
4422 self.advance();
4423 let inner = self.parse_select_stmt()?;
4424 if !matches!(self.peek(), Token::RParen) {
4425 return Err(self.err(format!(
4426 "expected ')' after EXISTS-subquery, got {:?}",
4427 self.peek()
4428 )));
4429 }
4430 self.advance();
4431 let Statement::Select(s) = inner else {
4432 unreachable!("parse_select_stmt returns Select")
4433 };
4434 Ok(Expr::Exists {
4435 subquery: Box::new(s),
4436 negated,
4437 })
4438 }
4439
4440 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4441 self.advance(); if !matches!(self.peek(), Token::LParen) {
4443 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
4444 }
4445 self.advance();
4446 if matches!(self.peek(), Token::Select) {
4448 let inner = self.parse_select_stmt()?;
4449 if !matches!(self.peek(), Token::RParen) {
4450 return Err(self.err(format!(
4451 "expected ')' after IN-subquery, got {:?}",
4452 self.peek()
4453 )));
4454 }
4455 self.advance();
4456 let Statement::Select(s) = inner else {
4457 unreachable!("parse_select_stmt always returns Statement::Select")
4458 };
4459 return Ok(Expr::InSubquery {
4460 expr: Box::new(expr),
4461 subquery: Box::new(s),
4462 negated,
4463 });
4464 }
4465 let mut elements = Vec::new();
4466 if !matches!(self.peek(), Token::RParen) {
4467 loop {
4468 elements.push(self.parse_expr(0)?);
4469 match self.peek() {
4470 Token::Comma => {
4471 self.advance();
4472 }
4473 Token::RParen => break,
4474 other => {
4475 return Err(
4476 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
4477 );
4478 }
4479 }
4480 }
4481 }
4482 self.advance(); let target = Box::new(expr);
4484 let combined = if elements.is_empty() {
4485 Expr::Literal(Literal::Bool(false))
4486 } else {
4487 let mut iter = elements.into_iter();
4488 let first = iter.next().unwrap();
4489 let mut acc = Expr::Binary {
4490 lhs: target.clone(),
4491 op: BinOp::Eq,
4492 rhs: Box::new(first),
4493 };
4494 for elt in iter {
4495 acc = Expr::Binary {
4496 lhs: Box::new(acc),
4497 op: BinOp::Or,
4498 rhs: Box::new(Expr::Binary {
4499 lhs: target.clone(),
4500 op: BinOp::Eq,
4501 rhs: Box::new(elt),
4502 }),
4503 };
4504 }
4505 acc
4506 };
4507 Ok(maybe_not(combined, negated))
4508 }
4509
4510 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
4518 if !matches!(self.peek(), Token::LParen) {
4519 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
4520 }
4521 self.advance();
4522 let field_name = self.expect_ident_like()?;
4523 let field = match field_name.to_ascii_lowercase().as_str() {
4524 "year" => ExtractField::Year,
4525 "month" => ExtractField::Month,
4526 "day" => ExtractField::Day,
4527 "hour" => ExtractField::Hour,
4528 "minute" => ExtractField::Minute,
4529 "second" => ExtractField::Second,
4530 "microsecond" | "microseconds" => ExtractField::Microsecond,
4531 other => {
4532 return Err(self.err(format!(
4533 "unknown EXTRACT field {other:?}; \
4534 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
4535 )));
4536 }
4537 };
4538 if !matches!(self.peek(), Token::From) {
4539 return Err(self.err(format!(
4540 "expected FROM after EXTRACT field, got {:?}",
4541 self.peek()
4542 )));
4543 }
4544 self.advance();
4545 let source = self.parse_expr(0)?;
4546 if !matches!(self.peek(), Token::RParen) {
4547 return Err(self.err(format!(
4548 "expected ')' to close EXTRACT, got {:?}",
4549 self.peek()
4550 )));
4551 }
4552 self.advance();
4553 Ok(Expr::Extract {
4554 field,
4555 source: Box::new(source),
4556 })
4557 }
4558
4559 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
4564 let tok = self.advance();
4565 let Token::String(text) = tok else {
4566 return Err(self.err(format!(
4567 "expected string literal after INTERVAL, got {tok:?}"
4568 )));
4569 };
4570 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
4571 message: format!(
4572 "cannot parse INTERVAL {text:?}; \
4573 expected `<n> <unit> [<n> <unit> ...]` with units \
4574 microsecond[s], millisecond[s], second[s], minute[s], \
4575 hour[s], day[s], week[s], month[s], year[s]"
4576 ),
4577 token_pos: self.pos.saturating_sub(1),
4578 })?;
4579 Ok(Expr::Literal(Literal::Interval {
4580 months,
4581 micros,
4582 text,
4583 }))
4584 }
4585
4586 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
4587 let mut elems = Vec::new();
4588 if matches!(self.peek(), Token::RBracket) {
4589 self.advance();
4590 return Ok(Expr::Literal(Literal::Vector(elems)));
4591 }
4592 loop {
4593 let e = self.parse_expr(0)?;
4594 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
4595 message: format!("vector element must be a numeric literal, got {e:?}"),
4596 token_pos: self.pos,
4597 })?;
4598 elems.push(x);
4599 match self.peek() {
4600 Token::Comma => {
4601 self.advance();
4602 }
4603 Token::RBracket => {
4604 self.advance();
4605 break;
4606 }
4607 other => {
4608 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
4609 }
4610 }
4611 }
4612 Ok(Expr::Literal(Literal::Vector(elems)))
4613 }
4614
4615 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
4624 let Token::Ident(s) = self.peek().clone() else {
4625 return NullTreatment::Respect;
4626 };
4627 let is_ignore = s.eq_ignore_ascii_case("ignore");
4628 let is_respect = s.eq_ignore_ascii_case("respect");
4629 if !is_ignore && !is_respect {
4630 return NullTreatment::Respect;
4631 }
4632 if self.pos + 1 < self.tokens.len()
4635 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
4636 && s2.eq_ignore_ascii_case("nulls")
4637 {
4638 self.advance();
4639 self.advance();
4640 return if is_ignore {
4641 NullTreatment::Ignore
4642 } else {
4643 NullTreatment::Respect
4644 };
4645 }
4646 NullTreatment::Respect
4647 }
4648
4649 #[allow(clippy::type_complexity)] fn parse_over_clause(
4652 &mut self,
4653 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
4654 if !matches!(self.peek(), Token::LParen) {
4655 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
4656 }
4657 self.advance();
4658 let mut partition_by = Vec::new();
4659 let mut order_by = Vec::new();
4660 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4662 && s.eq_ignore_ascii_case("partition")
4663 {
4664 self.advance();
4665 if !matches!(self.peek(), Token::By) {
4666 return Err(self.err(format!(
4667 "expected BY after PARTITION, got {:?}",
4668 self.peek()
4669 )));
4670 }
4671 self.advance();
4672 loop {
4673 partition_by.push(self.parse_expr(0)?);
4674 if matches!(self.peek(), Token::Comma) {
4675 self.advance();
4676 continue;
4677 }
4678 break;
4679 }
4680 }
4681 if matches!(self.peek(), Token::Order) {
4683 self.advance();
4684 if !matches!(self.peek(), Token::By) {
4685 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4686 }
4687 self.advance();
4688 loop {
4689 let e = self.parse_expr(0)?;
4690 let desc = if matches!(self.peek(), Token::Desc) {
4691 self.advance();
4692 true
4693 } else if matches!(self.peek(), Token::Asc) {
4694 self.advance();
4695 false
4696 } else {
4697 false
4698 };
4699 order_by.push((e, desc));
4700 if matches!(self.peek(), Token::Comma) {
4701 self.advance();
4702 continue;
4703 }
4704 break;
4705 }
4706 }
4707 let mut frame: Option<WindowFrame> = None;
4711 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
4712 let kind = if s.eq_ignore_ascii_case("rows") {
4713 Some(FrameKind::Rows)
4714 } else if s.eq_ignore_ascii_case("range") {
4715 Some(FrameKind::Range)
4716 } else {
4717 None
4718 };
4719 if let Some(kind) = kind {
4720 self.advance();
4721 frame = Some(self.parse_frame_tail(kind)?);
4722 }
4723 }
4724 if !matches!(self.peek(), Token::RParen) {
4725 return Err(self.err(format!(
4726 "expected ')' to close OVER clause, got {:?}",
4727 self.peek()
4728 )));
4729 }
4730 self.advance();
4731 Ok((partition_by, order_by, frame))
4732 }
4733
4734 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
4740 if matches!(self.peek(), Token::Between) {
4741 self.advance();
4742 let start = self.parse_frame_bound()?;
4743 if !matches!(self.peek(), Token::And) {
4744 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
4745 }
4746 self.advance();
4747 let end = self.parse_frame_bound()?;
4748 Ok(WindowFrame {
4749 kind,
4750 start,
4751 end: Some(end),
4752 })
4753 } else {
4754 let start = self.parse_frame_bound()?;
4755 Ok(WindowFrame {
4756 kind,
4757 start,
4758 end: None,
4759 })
4760 }
4761 }
4762
4763 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
4766 if let Token::Integer(n) = *self.peek() {
4768 self.advance();
4769 let n: u64 = u64::try_from(n).map_err(|_| {
4770 self.err(format!(
4771 "invalid frame offset {n} — expected non-negative integer"
4772 ))
4773 })?;
4774 let dir = self.expect_ident_like()?;
4775 return if dir.eq_ignore_ascii_case("preceding") {
4776 Ok(FrameBound::OffsetPreceding(n))
4777 } else if dir.eq_ignore_ascii_case("following") {
4778 Ok(FrameBound::OffsetFollowing(n))
4779 } else {
4780 Err(self.err(format!(
4781 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
4782 )))
4783 };
4784 }
4785 let first = self.expect_ident_like()?;
4786 if first.eq_ignore_ascii_case("unbounded") {
4787 let dir = self.expect_ident_like()?;
4788 return if dir.eq_ignore_ascii_case("preceding") {
4789 Ok(FrameBound::UnboundedPreceding)
4790 } else if dir.eq_ignore_ascii_case("following") {
4791 Ok(FrameBound::UnboundedFollowing)
4792 } else {
4793 Err(self.err(format!(
4794 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
4795 )))
4796 };
4797 }
4798 if first.eq_ignore_ascii_case("current") {
4799 let row = self.expect_ident_like()?;
4800 if !row.eq_ignore_ascii_case("row") {
4801 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
4802 }
4803 return Ok(FrameBound::CurrentRow);
4804 }
4805 Err(self.err(format!(
4806 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
4807 )))
4808 }
4809
4810 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
4811 if matches!(self.peek(), Token::Dot) {
4812 self.advance();
4813 let name = self.expect_ident_like()?;
4814 return Ok(Expr::Column(ColumnName {
4815 qualifier: Some(first),
4816 name,
4817 }));
4818 }
4819 if matches!(self.peek(), Token::LParen) {
4820 self.advance();
4821 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
4825 self.advance();
4826 if !matches!(self.peek(), Token::RParen) {
4827 return Err(self.err(format!(
4828 "expected ')' after COUNT(*), got {:?}",
4829 self.peek()
4830 )));
4831 }
4832 self.advance();
4833 let null_treatment = self.parse_null_treatment_modifier();
4835 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4836 && s.eq_ignore_ascii_case("over")
4837 {
4838 self.advance();
4839 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4840 return Ok(Expr::WindowFunction {
4841 name: "count_star".into(),
4842 args: Vec::new(),
4843 partition_by,
4844 order_by,
4845 frame,
4846 null_treatment,
4847 });
4848 }
4849 return Ok(Expr::FunctionCall {
4850 name: "count_star".into(),
4851 args: Vec::new(),
4852 });
4853 }
4854 let mut args = Vec::new();
4856 if !matches!(self.peek(), Token::RParen) {
4857 loop {
4858 args.push(self.parse_expr(0)?);
4859 match self.peek() {
4860 Token::Comma => {
4861 self.advance();
4862 }
4863 Token::RParen => break,
4864 other => {
4865 return Err(self.err(format!(
4866 "expected ',' or ')' in function args, got {other:?}"
4867 )));
4868 }
4869 }
4870 }
4871 }
4872 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
4880 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4881 && s.eq_ignore_ascii_case("over")
4882 {
4883 self.advance();
4884 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4885 return Ok(Expr::WindowFunction {
4886 name: first,
4887 args,
4888 partition_by,
4889 order_by,
4890 frame,
4891 null_treatment,
4892 });
4893 }
4894 return Ok(Expr::FunctionCall { name: first, args });
4895 }
4896 let lc = first.to_ascii_lowercase();
4902 if matches!(
4903 lc.as_str(),
4904 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
4905 ) {
4906 return Ok(Expr::FunctionCall {
4907 name: lc,
4908 args: Vec::new(),
4909 });
4910 }
4911 Ok(Expr::Column(ColumnName {
4912 qualifier: None,
4913 name: first,
4914 }))
4915 }
4916}
4917
4918fn extract_first_column(expr: &Expr) -> Option<String> {
4926 match expr {
4927 Expr::Column(cn) => Some(cn.name.clone()),
4928 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
4929 Expr::Binary { lhs, rhs, .. } => {
4930 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
4931 }
4932 Expr::Unary { expr: e, .. } => extract_first_column(e),
4933 _ => None,
4934 }
4935}
4936
4937fn maybe_not(expr: Expr, negated: bool) -> Expr {
4938 if negated {
4939 Expr::Unary {
4940 op: UnOp::Not,
4941 expr: Box::new(expr),
4942 }
4943 } else {
4944 expr
4945 }
4946}
4947
4948fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
4949 let pair = match tok {
4950 Token::Or => (BinOp::Or, 1),
4951 Token::And => (BinOp::And, 2),
4952 Token::Eq => (BinOp::Eq, 4),
4953 Token::NotEq => (BinOp::NotEq, 4),
4954 Token::Lt => (BinOp::Lt, 4),
4955 Token::LtEq => (BinOp::LtEq, 4),
4956 Token::Gt => (BinOp::Gt, 4),
4957 Token::GtEq => (BinOp::GtEq, 4),
4958 Token::L2Distance => (BinOp::L2Distance, 5),
4961 Token::InnerProduct => (BinOp::InnerProduct, 5),
4962 Token::CosineDistance => (BinOp::CosineDistance, 5),
4963 Token::Plus => (BinOp::Add, 6),
4964 Token::Minus => (BinOp::Sub, 6),
4965 Token::Concat => (BinOp::Concat, 6),
4968 Token::Star => (BinOp::Mul, 7),
4969 Token::Slash => (BinOp::Div, 7),
4970 Token::JsonGet => (BinOp::JsonGet, 7),
4974 Token::JsonGetText => (BinOp::JsonGetText, 7),
4975 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
4976 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
4977 Token::JsonContains => (BinOp::JsonContains, 7),
4978 Token::TsMatch => (BinOp::TsMatch, 4),
4982 _ => return None,
4983 };
4984 Some(pair)
4985}
4986
4987#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
4988fn extract_numeric_literal(e: &Expr) -> Option<f32> {
4993 match e {
4994 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
4995 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
4996 Expr::Unary {
4997 op: UnOp::Neg,
4998 expr,
4999 } => extract_numeric_literal(expr).map(|x| -x),
5000 _ => None,
5001 }
5002}
5003
5004pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
5012 let parts: Vec<&str> = s.split_whitespace().collect();
5013 if parts.is_empty() || !parts.len().is_multiple_of(2) {
5014 return None;
5015 }
5016 let mut months: i32 = 0;
5017 let mut micros: i64 = 0;
5018 let mut i = 0;
5019 while i < parts.len() {
5020 let n: i64 = parts[i].parse().ok()?;
5021 let unit = parts[i + 1].to_ascii_lowercase();
5022 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
5023 match unit_stripped {
5024 "microsecond" => micros = micros.checked_add(n)?,
5025 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
5026 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
5027 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
5028 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
5029 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
5030 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
5031 "month" => {
5032 let n32 = i32::try_from(n).ok()?;
5033 months = months.checked_add(n32)?;
5034 }
5035 "year" => {
5036 let n32 = i32::try_from(n).ok()?;
5037 months = months.checked_add(n32.checked_mul(12)?)?;
5038 }
5039 _ => return None,
5040 }
5041 i += 2;
5042 }
5043 Some((months, micros))
5044}
5045
5046fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
5057 Some(match ident.to_ascii_lowercase().as_str() {
5058 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
5059 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
5060 "bigint" => ColumnTypeName::BigInt,
5061 "float" | "double" | "real" => ColumnTypeName::Float,
5062 "text" => ColumnTypeName::Text,
5063 "bool" | "boolean" => ColumnTypeName::Bool,
5064 "date" => ColumnTypeName::Date,
5065 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
5066 "timestamptz" => ColumnTypeName::Timestamptz,
5067 "json" => ColumnTypeName::Json,
5068 "jsonb" => ColumnTypeName::Jsonb,
5069 "bytea" | "bytes" => ColumnTypeName::Bytes,
5070 "tsvector" => ColumnTypeName::TsVector,
5071 "tsquery" => ColumnTypeName::TsQuery,
5072 _ => return None,
5073 })
5074}
5075
5076pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5103 parse_plpgsql_body(body)
5104}
5105
5106fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5107 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
5111 message: alloc::format!("plpgsql body lex error: {e}"),
5112 token_pos: 0,
5113 })?;
5114 let mut parser = Parser::new(tokens);
5115 parser.parse_plpgsql_block()
5116}
5117
5118#[cfg(test)]
5119mod tests {
5120 use super::*;
5121 use alloc::string::ToString;
5122
5123 fn parse(s: &str) -> Statement {
5124 parse_statement(s).expect("parse ok")
5125 }
5126
5127 fn lit_int(n: i64) -> Expr {
5128 Expr::Literal(Literal::Integer(n))
5129 }
5130
5131 fn col(name: &str) -> Expr {
5132 Expr::Column(ColumnName {
5133 qualifier: None,
5134 name: name.into(),
5135 })
5136 }
5137
5138 #[test]
5139 fn select_single_integer() {
5140 let s = parse("SELECT 1");
5141 let Statement::Select(s) = s else {
5142 panic!("expected SELECT")
5143 };
5144 assert_eq!(s.items.len(), 1);
5145 assert!(s.from.is_none());
5146 assert!(s.where_.is_none());
5147 }
5148
5149 #[test]
5150 fn select_multiple_literal_kinds() {
5151 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
5152 let Statement::Select(s) = s else {
5153 panic!("expected SELECT")
5154 };
5155 assert_eq!(s.items.len(), 5);
5156 }
5157
5158 #[test]
5159 fn select_wildcard_from_table() {
5160 let s = parse("SELECT * FROM users");
5161 let Statement::Select(s) = s else {
5162 panic!("expected SELECT")
5163 };
5164 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
5165 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
5166 }
5167
5168 #[test]
5169 fn select_with_table_alias() {
5170 let s = parse("SELECT * FROM users AS u");
5171 let Statement::Select(s) = s else {
5172 panic!("expected SELECT")
5173 };
5174 let t = &s.from.as_ref().unwrap().primary;
5175 assert_eq!(t.name, "users");
5176 assert_eq!(t.alias.as_deref(), Some("u"));
5177 }
5178
5179 #[test]
5180 fn select_with_where_eq() {
5181 let s = parse("SELECT a FROM t WHERE a = 1");
5182 let Statement::Select(s) = s else {
5183 panic!("expected SELECT")
5184 };
5185 let w = s.where_.unwrap();
5186 assert_eq!(
5187 w,
5188 Expr::Binary {
5189 lhs: Box::new(col("a")),
5190 op: BinOp::Eq,
5191 rhs: Box::new(lit_int(1)),
5192 }
5193 );
5194 }
5195
5196 #[test]
5197 fn arithmetic_precedence() {
5198 let s = parse("SELECT 1 + 2 * 3");
5199 let Statement::Select(s) = s else {
5200 panic!("expected SELECT")
5201 };
5202 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5203 panic!("wildcard?")
5204 };
5205 assert_eq!(
5206 expr,
5207 &Expr::Binary {
5208 lhs: Box::new(lit_int(1)),
5209 op: BinOp::Add,
5210 rhs: Box::new(Expr::Binary {
5211 lhs: Box::new(lit_int(2)),
5212 op: BinOp::Mul,
5213 rhs: Box::new(lit_int(3)),
5214 }),
5215 }
5216 );
5217 }
5218
5219 #[test]
5220 fn parentheses_override_precedence() {
5221 let s = parse("SELECT (1 + 2) * 3");
5222 let Statement::Select(s) = s else {
5223 panic!("expected SELECT")
5224 };
5225 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5226 panic!()
5227 };
5228 assert_eq!(
5229 expr,
5230 &Expr::Binary {
5231 lhs: Box::new(Expr::Binary {
5232 lhs: Box::new(lit_int(1)),
5233 op: BinOp::Add,
5234 rhs: Box::new(lit_int(2)),
5235 }),
5236 op: BinOp::Mul,
5237 rhs: Box::new(lit_int(3)),
5238 }
5239 );
5240 }
5241
5242 #[test]
5243 fn not_binds_below_comparison() {
5244 let s = parse("SELECT NOT a = 1 FROM t");
5246 let Statement::Select(s) = s else {
5247 panic!("expected SELECT")
5248 };
5249 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5250 panic!()
5251 };
5252 assert_eq!(
5253 expr,
5254 &Expr::Unary {
5255 op: UnOp::Not,
5256 expr: Box::new(Expr::Binary {
5257 lhs: Box::new(col("a")),
5258 op: BinOp::Eq,
5259 rhs: Box::new(lit_int(1)),
5260 }),
5261 }
5262 );
5263 }
5264
5265 #[test]
5266 fn unary_minus_binds_above_multiplication() {
5267 let s = parse("SELECT -a * 2 FROM t");
5269 let Statement::Select(s) = s else {
5270 panic!("expected SELECT")
5271 };
5272 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5273 panic!()
5274 };
5275 assert_eq!(
5276 expr,
5277 &Expr::Binary {
5278 lhs: Box::new(Expr::Unary {
5279 op: UnOp::Neg,
5280 expr: Box::new(col("a")),
5281 }),
5282 op: BinOp::Mul,
5283 rhs: Box::new(lit_int(2)),
5284 }
5285 );
5286 }
5287
5288 #[test]
5289 fn qualified_column() {
5290 let s = parse("SELECT t.col FROM t");
5291 let Statement::Select(s) = s else {
5292 panic!("expected SELECT")
5293 };
5294 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5295 panic!()
5296 };
5297 assert_eq!(
5298 expr,
5299 &Expr::Column(ColumnName {
5300 qualifier: Some("t".into()),
5301 name: "col".into()
5302 })
5303 );
5304 }
5305
5306 #[test]
5307 fn select_item_alias_with_as() {
5308 let s = parse("SELECT a AS y FROM t");
5309 let Statement::Select(s) = s else {
5310 panic!("expected SELECT")
5311 };
5312 let SelectItem::Expr { alias, .. } = &s.items[0] else {
5313 panic!()
5314 };
5315 assert_eq!(alias.as_deref(), Some("y"));
5316 }
5317
5318 #[test]
5319 fn trailing_semicolon_accepted() {
5320 let s = parse("SELECT 1;");
5321 let Statement::Select(s) = s else {
5322 panic!("expected SELECT")
5323 };
5324 assert_eq!(s.items.len(), 1);
5325 }
5326
5327 #[test]
5328 fn boolean_chain_with_and_or_not() {
5329 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
5331 let Statement::Select(s) = s else {
5332 panic!("expected SELECT")
5333 };
5334 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5335 panic!()
5336 };
5337 let expected = Expr::Binary {
5338 lhs: Box::new(Expr::Unary {
5339 op: UnOp::Not,
5340 expr: Box::new(col("a")),
5341 }),
5342 op: BinOp::Or,
5343 rhs: Box::new(Expr::Binary {
5344 lhs: Box::new(col("b")),
5345 op: BinOp::And,
5346 rhs: Box::new(Expr::Unary {
5347 op: UnOp::Not,
5348 expr: Box::new(col("c")),
5349 }),
5350 }),
5351 };
5352 assert_eq!(expr, &expected);
5353 }
5354
5355 #[test]
5356 fn empty_input_errors() {
5357 let err = parse_statement("").unwrap_err();
5358 assert!(err.message.contains("SELECT"));
5359 }
5360
5361 #[test]
5362 fn unmatched_paren_errors() {
5363 assert!(parse_statement("SELECT (1 + 2").is_err());
5364 }
5365
5366 #[test]
5367 fn display_round_trip_simple_select() {
5368 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
5369 let text = original.to_string();
5370 let again = parse_statement(&text).expect("re-parse");
5371 assert_eq!(original, again);
5372 }
5373
5374 #[test]
5377 fn create_table_single_column() {
5378 let s = parse("CREATE TABLE foo (a INT)");
5379 let Statement::CreateTable(c) = s else {
5380 panic!("expected CreateTable")
5381 };
5382 assert_eq!(c.name, "foo");
5383 assert_eq!(c.columns.len(), 1);
5384 assert_eq!(c.columns[0].name, "a");
5385 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5386 assert!(c.columns[0].nullable);
5387 }
5388
5389 #[test]
5390 fn create_table_multi_column_with_not_null_mix() {
5391 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
5392 let Statement::CreateTable(c) = s else {
5393 panic!()
5394 };
5395 assert_eq!(c.columns.len(), 4);
5396 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5397 assert!(!c.columns[0].nullable);
5398 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
5399 assert!(c.columns[1].nullable);
5400 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
5401 assert!(!c.columns[2].nullable);
5402 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
5403 }
5404
5405 #[test]
5406 fn create_table_bigint_supported() {
5407 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
5408 let Statement::CreateTable(c) = s else {
5409 panic!()
5410 };
5411 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
5412 }
5413
5414 #[test]
5415 fn create_table_vector_default_is_f32() {
5416 let s = parse("CREATE TABLE t (v VECTOR(128))");
5417 let Statement::CreateTable(c) = s else {
5418 panic!()
5419 };
5420 assert_eq!(
5421 c.columns[0].ty,
5422 ColumnTypeName::Vector {
5423 dim: 128,
5424 encoding: VecEncoding::F32,
5425 },
5426 );
5427 }
5428
5429 #[test]
5430 fn create_table_vector_using_sq8() {
5431 for sql in [
5434 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
5435 "CREATE TABLE t (v VECTOR(128) using sq8)",
5436 ] {
5437 let s = parse(sql);
5438 let Statement::CreateTable(c) = s else {
5439 panic!()
5440 };
5441 assert_eq!(
5442 c.columns[0].ty,
5443 ColumnTypeName::Vector {
5444 dim: 128,
5445 encoding: VecEncoding::Sq8,
5446 },
5447 "{sql}",
5448 );
5449 }
5450 }
5451
5452 #[test]
5453 fn create_table_vector_using_unknown_errors() {
5454 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
5455 assert!(
5456 err.message.contains("unknown vector encoding"),
5457 "got: {}",
5458 err.message
5459 );
5460 }
5461
5462 #[test]
5463 fn vector_using_sq8_display_roundtrips() {
5464 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
5467 let Statement::CreateTable(c) = s else {
5468 panic!()
5469 };
5470 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
5471 }
5472
5473 #[test]
5474 fn parser_recognises_placeholders() {
5475 use crate::ast::{Expr, SelectItem, Statement};
5476 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
5478 let Statement::Select(sel) = s else { panic!() };
5479 assert!(matches!(
5480 sel.items[0],
5481 SelectItem::Expr {
5482 expr: Expr::Placeholder(1),
5483 alias: None
5484 }
5485 ));
5486 let SelectItem::Expr {
5488 expr: Expr::Binary { lhs, rhs, .. },
5489 ..
5490 } = &sel.items[1]
5491 else {
5492 panic!()
5493 };
5494 assert!(matches!(**lhs, Expr::Placeholder(2)));
5495 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
5496 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
5498 panic!()
5499 };
5500 assert!(matches!(**rhs, Expr::Placeholder(3)));
5501 }
5502
5503 #[test]
5504 fn parser_rejects_dollar_zero() {
5505 assert!(parse_statement("SELECT $0").is_err());
5507 }
5508
5509 #[test]
5510 fn placeholder_display_roundtrips() {
5511 let s = parse("SELECT $42 FROM t");
5514 let printed = s.to_string();
5515 assert!(printed.contains("$42"));
5516 let again = parse(&printed);
5517 assert_eq!(s, again);
5518 }
5519
5520 #[test]
5521 fn alter_index_rebuild_bare() {
5522 use crate::ast::{AlterIndexTarget, Statement};
5523 let s = parse("ALTER INDEX my_idx REBUILD");
5524 let Statement::AlterIndex(a) = s else {
5525 panic!("expected AlterIndex, got {s:?}")
5526 };
5527 assert_eq!(a.name, "my_idx");
5528 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
5529 }
5530
5531 #[test]
5532 fn alter_index_rebuild_with_encoding() {
5533 use crate::ast::{AlterIndexTarget, Statement};
5534 for (sql, want) in [
5535 (
5536 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
5537 VecEncoding::F32,
5538 ),
5539 (
5540 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
5541 VecEncoding::Sq8,
5542 ),
5543 (
5544 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5545 VecEncoding::F16,
5546 ),
5547 ] {
5548 let s = parse(sql);
5549 let Statement::AlterIndex(a) = s else {
5550 panic!("{sql}: expected AlterIndex")
5551 };
5552 assert_eq!(a.name, "my_idx");
5553 assert_eq!(
5554 a.target,
5555 AlterIndexTarget::Rebuild {
5556 encoding: Some(want)
5557 },
5558 "{sql}"
5559 );
5560 }
5561 }
5562
5563 #[test]
5564 fn alter_index_rebuild_unknown_encoding_errors() {
5565 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
5566 assert!(
5567 err.message.contains("unknown vector encoding"),
5568 "got: {}",
5569 err.message
5570 );
5571 }
5572
5573 #[test]
5574 fn alter_index_rebuild_display_roundtrips() {
5575 for (input, want) in [
5576 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
5577 (
5578 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5579 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5580 ),
5581 (
5582 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5583 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5584 ),
5585 ] {
5586 let s = parse(input);
5587 assert_eq!(s.to_string(), want);
5588 }
5589 }
5590
5591 #[test]
5592 fn create_table_unknown_type_errors() {
5593 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
5596 assert!(err.message.contains("unsupported column type"));
5597 }
5598
5599 #[test]
5600 fn create_table_missing_table_keyword_errors() {
5601 assert!(parse_statement("CREATE x (a INT)").is_err());
5602 }
5603
5604 #[test]
5605 fn insert_single_value() {
5606 let s = parse("INSERT INTO foo VALUES (42)");
5607 let Statement::Insert(i) = s else {
5608 panic!("expected Insert")
5609 };
5610 assert_eq!(i.table, "foo");
5611 assert_eq!(i.rows.len(), 1);
5612 assert_eq!(i.rows[0].len(), 1);
5613 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
5614 }
5615
5616 #[test]
5617 fn insert_multi_value_with_mixed_literals() {
5618 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
5619 let Statement::Insert(i) = s else { panic!() };
5620 assert_eq!(i.rows.len(), 1);
5621 assert_eq!(i.rows[0].len(), 5);
5622 }
5623
5624 #[test]
5625 fn insert_missing_into_errors() {
5626 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
5627 }
5628
5629 #[test]
5630 fn create_table_round_trip() {
5631 let original =
5632 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
5633 let text = original.to_string();
5634 let again = parse_statement(&text).expect("re-parse");
5635 assert_eq!(original, again);
5636 }
5637
5638 #[test]
5639 fn insert_round_trip_with_negation_and_string() {
5640 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
5641 let text = original.to_string();
5642 let again = parse_statement(&text).expect("re-parse");
5643 assert_eq!(original, again);
5644 }
5645
5646 #[test]
5647 fn unknown_keyword_at_statement_start_errors() {
5648 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
5651 assert!(err.message.contains("expected SELECT"));
5652 }
5653
5654 #[test]
5657 fn create_index_basic() {
5658 let s = parse("CREATE INDEX idx_id ON users (id)");
5659 let Statement::CreateIndex(c) = s else {
5660 panic!("expected CreateIndex")
5661 };
5662 assert_eq!(c.name, "idx_id");
5663 assert_eq!(c.table, "users");
5664 assert_eq!(c.column, "id");
5665 }
5666
5667 #[test]
5668 fn create_index_missing_on_errors() {
5669 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
5670 }
5671
5672 #[test]
5673 fn create_index_missing_paren_errors() {
5674 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
5675 }
5676
5677 #[test]
5678 fn create_index_round_trip() {
5679 let original = parse("CREATE INDEX by_name ON users (name)");
5680 let again = parse_statement(&original.to_string()).unwrap();
5681 assert_eq!(original, again);
5682 }
5683
5684 #[test]
5687 fn create_unique_index_basic() {
5688 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
5689 let Statement::CreateIndex(c) = s else {
5690 panic!("expected CreateIndex");
5691 };
5692 assert!(c.is_unique);
5693 assert_eq!(c.column, "a");
5694 assert!(c.partial_predicate.is_none());
5695 }
5696
5697 #[test]
5698 fn create_unique_index_partial() {
5699 let s = parse(
5701 "CREATE UNIQUE INDEX idx_email_templates_user_default \
5702 ON email_templates (user_address) WHERE is_default = true",
5703 );
5704 let Statement::CreateIndex(c) = s else {
5705 panic!("expected CreateIndex");
5706 };
5707 assert!(c.is_unique);
5708 assert_eq!(c.table, "email_templates");
5709 assert_eq!(c.column, "user_address");
5710 assert!(c.partial_predicate.is_some());
5711 }
5712
5713 #[test]
5714 fn create_unique_index_composite_with_predicate() {
5715 let s = parse(
5717 "CREATE UNIQUE INDEX uq_calendar_events_instance \
5718 ON calendar_events (calendar_id, uid, recurrence_id) \
5719 WHERE recurrence_id IS NOT NULL",
5720 );
5721 let Statement::CreateIndex(c) = s else {
5722 panic!("expected CreateIndex");
5723 };
5724 assert!(c.is_unique);
5725 assert_eq!(c.column, "calendar_id");
5726 assert_eq!(
5727 c.extra_columns,
5728 vec!["uid".to_string(), "recurrence_id".to_string()]
5729 );
5730 assert!(c.partial_predicate.is_some());
5731 }
5732
5733 #[test]
5734 fn create_unique_index_using_btree_ok() {
5735 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
5736 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
5737 }
5738
5739 #[test]
5740 fn create_unique_index_using_hnsw_rejected() {
5741 let err =
5742 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
5743 assert!(err.message.contains("UNIQUE"), "{}", err.message);
5744 }
5745
5746 #[test]
5747 fn create_unique_index_round_trip() {
5748 let original = parse(
5749 "CREATE UNIQUE INDEX uq_calendar_events_master \
5750 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
5751 );
5752 let again = parse_statement(&original.to_string()).unwrap();
5753 assert_eq!(original, again);
5754 }
5755
5756 #[test]
5757 fn create_unique_without_index_errors() {
5758 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
5759 assert!(err.message.contains("INDEX"), "{}", err.message);
5760 }
5761
5762 #[test]
5765 fn create_table_bytea_column() {
5766 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
5767 let Statement::CreateTable(c) = s else {
5768 panic!("expected CreateTable");
5769 };
5770 assert_eq!(c.columns.len(), 2);
5771 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
5772 assert!(!c.columns[1].nullable);
5773 }
5774
5775 #[test]
5776 fn create_table_bytes_alias_column() {
5777 let s = parse("CREATE TABLE t (blob BYTES)");
5778 let Statement::CreateTable(c) = s else {
5779 panic!("expected CreateTable");
5780 };
5781 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
5782 }
5783
5784 #[test]
5785 fn bytea_round_trip_display() {
5786 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
5787 let again = parse_statement(&original.to_string()).unwrap();
5788 assert_eq!(original, again);
5789 }
5790
5791 #[test]
5794 fn begin_commit_rollback_parse_as_unit_variants() {
5795 assert_eq!(parse("BEGIN"), Statement::Begin);
5796 assert_eq!(parse("COMMIT"), Statement::Commit);
5797 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
5798 assert_eq!(parse("BEGIN;"), Statement::Begin);
5800 }
5801
5802 #[test]
5805 fn inner_product_binop_parses() {
5806 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
5807 let Statement::Select(s) = s else { panic!() };
5808 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5809 panic!()
5810 };
5811 assert!(matches!(
5812 expr,
5813 Expr::Binary {
5814 op: BinOp::InnerProduct,
5815 ..
5816 }
5817 ));
5818 }
5819
5820 #[test]
5821 fn cosine_distance_binop_parses() {
5822 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
5823 let Statement::Select(s) = s else { panic!() };
5824 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5825 panic!()
5826 };
5827 assert!(matches!(
5828 expr,
5829 Expr::Binary {
5830 op: BinOp::CosineDistance,
5831 ..
5832 }
5833 ));
5834 }
5835
5836 #[test]
5837 fn vector_cast_postfix_wraps_string_literal() {
5838 let s = parse("SELECT '[1,2,3]'::vector FROM t");
5839 let Statement::Select(s) = s else { panic!() };
5840 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5841 panic!()
5842 };
5843 assert!(matches!(
5844 expr,
5845 Expr::Cast {
5846 target: CastTarget::Vector,
5847 ..
5848 }
5849 ));
5850 }
5851
5852 #[test]
5853 fn unsupported_cast_target_errors() {
5854 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
5856 assert!(err.message.contains("unsupported cast target"));
5857 }
5858
5859 #[test]
5860 fn tx_statements_round_trip() {
5861 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
5862 let original = parse(q);
5863 let again = parse_statement(&original.to_string()).unwrap();
5864 assert_eq!(original, again);
5865 }
5866 }
5867
5868 #[test]
5869 fn interval_text_parsing_units() {
5870 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
5872 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
5873 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
5874 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
5875 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
5877 assert_eq!(
5878 parse_interval_text("1 day 2 hours"),
5879 Some((0, 86_400_000_000 + 7_200_000_000))
5880 );
5881 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
5883 assert_eq!(parse_interval_text(""), None);
5885 assert_eq!(parse_interval_text("garbage"), None);
5886 assert_eq!(parse_interval_text("1 fortnight"), None);
5887 assert_eq!(parse_interval_text("1"), None);
5888 }
5889
5890 #[test]
5891 fn interval_literal_roundtrips_via_display() {
5892 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
5893 let s = parsed.to_string();
5894 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
5896 let again = parse_statement(&s).unwrap();
5898 assert_eq!(parsed, again);
5899 }
5900
5901 #[test]
5904 fn parser_recognises_create_publication_bare() {
5905 let s = parse("CREATE PUBLICATION pub_a");
5906 let Statement::CreatePublication(p) = s else {
5907 panic!("expected CreatePublication, got {s:?}")
5908 };
5909 assert_eq!(p.name, "pub_a");
5910 assert_eq!(p.scope, PublicationScope::AllTables);
5911 }
5912
5913 #[test]
5914 fn parser_recognises_create_publication_for_all_tables() {
5915 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
5916 let Statement::CreatePublication(p) = s else {
5917 panic!("expected CreatePublication, got {s:?}")
5918 };
5919 assert_eq!(p.name, "pub_a");
5920 assert_eq!(p.scope, PublicationScope::AllTables);
5921 }
5922
5923 #[test]
5924 fn parser_recognises_drop_publication() {
5925 let s = parse("DROP PUBLICATION pub_a");
5926 let Statement::DropPublication(name) = s else {
5927 panic!("expected DropPublication, got {s:?}")
5928 };
5929 assert_eq!(name, "pub_a");
5930 }
5931
5932 #[test]
5933 fn parser_recognises_for_table_list() {
5934 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
5935 let Statement::CreatePublication(p) = s else {
5936 panic!("expected CreatePublication, got {s:?}")
5937 };
5938 assert_eq!(p.name, "pub_a");
5939 let PublicationScope::ForTables(ts) = p.scope else {
5940 panic!("expected ForTables scope")
5941 };
5942 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
5943 }
5944
5945 #[test]
5946 fn parser_recognises_for_tables_plural() {
5947 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
5949 let Statement::CreatePublication(p) = s else {
5950 panic!("expected CreatePublication, got {s:?}")
5951 };
5952 let PublicationScope::ForTables(ts) = p.scope else {
5953 panic!("expected ForTables")
5954 };
5955 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5956 }
5957
5958 #[test]
5959 fn parser_recognises_for_all_tables_except_list() {
5960 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
5961 let Statement::CreatePublication(p) = s else {
5962 panic!()
5963 };
5964 let PublicationScope::AllTablesExcept(ts) = p.scope else {
5965 panic!("expected AllTablesExcept")
5966 };
5967 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5968 }
5969
5970 #[test]
5971 fn parser_rejects_for_table_with_empty_list() {
5972 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
5974 .expect_err("must error on empty list");
5975 assert!(!err.message.is_empty());
5978 }
5979
5980 #[test]
5981 fn parser_recognises_show_publications() {
5982 let s = parse("SHOW PUBLICATIONS");
5985 assert!(matches!(s, Statement::ShowPublications));
5986 }
5987
5988 #[test]
5991 fn parser_recognises_create_subscription_single_publication() {
5992 let s = parse(
5993 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
5994 );
5995 let Statement::CreateSubscription(c) = s else {
5996 panic!("expected CreateSubscription, got {s:?}")
5997 };
5998 assert_eq!(c.name, "sub_a");
5999 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
6000 assert_eq!(c.publications, alloc::vec!["pub_a"]);
6001 }
6002
6003 #[test]
6004 fn parser_recognises_create_subscription_multi_publication() {
6005 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
6006 let Statement::CreateSubscription(c) = s else {
6007 panic!()
6008 };
6009 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
6010 }
6011
6012 #[test]
6013 fn parser_rejects_create_subscription_missing_connection() {
6014 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
6015 .expect_err("must error on missing CONNECTION");
6016 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
6017 }
6018
6019 #[test]
6020 fn parser_rejects_create_subscription_missing_publication() {
6021 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
6022 .expect_err("must error on missing PUBLICATION");
6023 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
6024 }
6025
6026 #[test]
6027 fn parser_recognises_drop_subscription() {
6028 let s = parse("DROP SUBSCRIPTION sub_a");
6029 let Statement::DropSubscription(name) = s else {
6030 panic!("expected DropSubscription, got {s:?}")
6031 };
6032 assert_eq!(name, "sub_a");
6033 }
6034
6035 #[test]
6036 fn parser_recognises_show_subscriptions() {
6037 let s = parse("SHOW SUBSCRIPTIONS");
6038 assert!(matches!(s, Statement::ShowSubscriptions));
6039 }
6040
6041 #[test]
6042 fn parser_recognises_wait_for_wal_position_no_timeout() {
6043 let s = parse("WAIT FOR WAL POSITION 12345");
6044 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6045 panic!("expected WaitForWalPosition, got {s:?}")
6046 };
6047 assert_eq!(pos, 12345);
6048 assert!(timeout_ms.is_none());
6049 }
6050
6051 #[test]
6052 fn parser_recognises_wait_for_wal_position_with_timeout() {
6053 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
6054 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6055 panic!()
6056 };
6057 assert_eq!(pos, 67890);
6058 assert_eq!(timeout_ms, Some(5000));
6059 }
6060
6061 #[test]
6062 fn parser_rejects_wait_with_negative_position() {
6063 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
6069 assert!(!err.message.is_empty());
6070 }
6071
6072 #[test]
6073 fn parser_recognises_bare_analyze() {
6074 let s = parse("ANALYZE");
6075 assert!(matches!(s, Statement::Analyze(None)));
6076 }
6077
6078 #[test]
6079 fn parser_recognises_analyze_with_table() {
6080 let s = parse("ANALYZE users");
6081 let Statement::Analyze(Some(name)) = s else {
6082 panic!("expected Analyze, got {s:?}")
6083 };
6084 assert_eq!(name, "users");
6085 }
6086
6087 #[test]
6088 fn parser_recognises_analyze_with_quoted_table() {
6089 let s = parse("ANALYZE \"Mixed Case\"");
6090 let Statement::Analyze(Some(name)) = s else {
6091 panic!()
6092 };
6093 assert_eq!(name, "Mixed Case");
6094 }
6095
6096 #[test]
6097 fn parser_rejects_analyze_with_garbage_token() {
6098 let err = parse_statement("ANALYZE 42").expect_err("must error");
6099 assert!(!err.message.is_empty());
6100 }
6101
6102 #[test]
6103 fn analyze_display_roundtrips() {
6104 for sql in ["ANALYZE", "ANALYZE users"] {
6105 let s = parse(sql);
6106 let printed = s.to_string();
6107 let again = parse_statement(&printed)
6108 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6109 assert_eq!(s, again);
6110 }
6111 }
6112
6113 #[test]
6114 fn wait_for_display_roundtrips() {
6115 for sql in [
6116 "WAIT FOR WAL POSITION 12345",
6117 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
6118 ] {
6119 let s = parse(sql);
6120 let printed = s.to_string();
6121 let again = parse_statement(&printed)
6122 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6123 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6124 }
6125 }
6126
6127 #[test]
6128 fn subscription_ddl_display_roundtrips() {
6129 for sql in [
6130 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
6131 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
6132 "DROP SUBSCRIPTION sub_a",
6133 "SHOW SUBSCRIPTIONS",
6134 ] {
6135 let s = parse(sql);
6136 let printed = s.to_string();
6137 let again = parse_statement(&printed)
6138 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6139 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6140 }
6141 }
6142
6143 #[test]
6144 fn parser_drop_dispatches_user_vs_publication() {
6145 let s = parse("DROP USER 'alice'");
6148 let Statement::DropUser(name) = s else {
6149 panic!("expected DropUser, got {s:?}")
6150 };
6151 assert_eq!(name, "alice");
6152 let s = parse("DROP PUBLICATION p1");
6154 assert!(matches!(s, Statement::DropPublication(_)));
6155 }
6156
6157 #[test]
6158 fn publication_ddl_display_roundtrips() {
6159 for sql in [
6162 "CREATE PUBLICATION pub_a",
6163 "CREATE PUBLICATION pub_a FOR ALL TABLES",
6164 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
6165 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
6166 "DROP PUBLICATION pub_a",
6167 "SHOW PUBLICATIONS",
6168 ] {
6169 let s = parse(sql);
6170 let printed = s.to_string();
6171 let again = parse_statement(&printed)
6172 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6173 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6174 }
6175 }
6176
6177 #[test]
6180 fn create_function_returns_trigger_plpgsql_minimal() {
6181 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
6182 let s = parse(sql);
6183 let Statement::CreateFunction(f) = s else {
6184 panic!("expected CreateFunction");
6185 };
6186 assert_eq!(f.name, "noop");
6187 assert!(!f.or_replace);
6188 assert!(f.args.is_empty());
6189 assert!(matches!(f.returns, FunctionReturn::Trigger));
6190 assert_eq!(f.language, "plpgsql");
6191 let FunctionBody::PlPgSql(block) = f.body else {
6192 panic!("expected PlPgSql body");
6193 };
6194 assert_eq!(block.statements.len(), 1);
6195 assert!(matches!(
6196 block.statements[0],
6197 PlPgSqlStmt::Return(ReturnTarget::New)
6198 ));
6199 }
6200
6201 #[test]
6202 fn create_function_or_replace_with_assignment() {
6203 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
6206BEGIN
6207 NEW.search_vector := to_tsvector('english', NEW.subject);
6208 RETURN NEW;
6209END;
6210$$";
6211 let s = parse(sql);
6212 let Statement::CreateFunction(f) = s else {
6213 panic!("expected CreateFunction");
6214 };
6215 assert!(f.or_replace);
6216 let FunctionBody::PlPgSql(block) = &f.body else {
6217 panic!("expected PlPgSql body");
6218 };
6219 assert_eq!(block.statements.len(), 2);
6220 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
6222 panic!("expected Assign as first stmt");
6223 };
6224 match target {
6225 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
6226 other => panic!("expected NEW.col, got {other:?}"),
6227 }
6228 assert!(matches!(
6230 block.statements[1],
6231 PlPgSqlStmt::Return(ReturnTarget::New)
6232 ));
6233 }
6234
6235 #[test]
6236 fn create_trigger_after_insert_or_update() {
6237 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
6238 let s = parse(sql);
6239 let Statement::CreateTrigger(t) = s else {
6240 panic!("expected CreateTrigger");
6241 };
6242 assert_eq!(t.name, "tg");
6243 assert_eq!(t.table, "messages");
6244 assert_eq!(t.timing, TriggerTiming::After);
6245 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
6246 assert_eq!(t.for_each, TriggerForEach::Row);
6247 assert_eq!(t.function, "update_sv");
6248 }
6249
6250 #[test]
6251 fn create_trigger_before_delete_execute_procedure_alias() {
6252 let sql =
6254 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
6255 let s = parse(sql);
6256 let Statement::CreateTrigger(t) = s else {
6257 panic!("expected CreateTrigger");
6258 };
6259 assert_eq!(t.timing, TriggerTiming::Before);
6260 assert_eq!(t.events, vec![TriggerEvent::Delete]);
6261 }
6262
6263 #[test]
6264 fn drop_trigger_if_exists_round_trips() {
6265 let s = Statement::DropTrigger {
6270 name: "tg".into(),
6271 table: "messages".into(),
6272 if_exists: true,
6273 };
6274 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
6275 }
6276
6277 #[test]
6278 fn trigger_ddl_display_roundtrips_through_parser() {
6279 for sql in [
6283 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
6284 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
6285 ] {
6286 let s = parse(sql);
6287 let printed = s.to_string();
6288 let again = parse_statement(&printed)
6289 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6290 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6291 }
6292 }
6293}