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 match self.advance() {
1874 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {}
1875 other => {
1876 return Err(self.err(alloc::format!(
1877 "expected CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
1878 )));
1879 }
1880 }
1881 let mut if_exists = false;
1883 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
1884 let n1 = self.tokens.get(self.pos + 1);
1885 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
1886 self.advance();
1887 self.advance();
1888 if_exists = true;
1889 }
1890 }
1891 let cname = self.expect_ident_like()?;
1892 if matches!(
1895 self.peek(),
1896 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
1897 || s.eq_ignore_ascii_case("restrict")
1898 ) {
1899 self.advance();
1900 }
1901 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
1902 name: cname,
1903 if_exists,
1904 }])
1905 }
1906 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
1907 self.advance();
1908 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
1909 self.advance();
1910 }
1911 let col_name = self.expect_ident_like()?;
1912 match self.peek() {
1913 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
1914 self.advance();
1915 }
1916 other => {
1917 return Err(self.err(alloc::format!(
1918 "expected TYPE after ALTER COLUMN <name>, got {other:?}"
1919 )));
1920 }
1921 }
1922 let new_type = self.parse_column_type_name()?;
1923 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
1924 {
1925 self.advance();
1926 Some(self.parse_expr(0)?)
1927 } else {
1928 None
1929 };
1930 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
1931 column: col_name,
1932 new_type,
1933 using,
1934 }])
1935 }
1936 other => Err(self.err(alloc::format!(
1937 "expected SET / ADD / DROP / ALTER in ALTER TABLE, got {other:?}"
1938 ))),
1939 }
1940 }
1941
1942 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
1944 match self.advance() {
1945 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
1946 other => Err(ParseError {
1947 message: format!("expected {kw:?}, got {other:?}"),
1948 token_pos: self.pos.saturating_sub(1),
1949 }),
1950 }
1951 }
1952
1953 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
1957 match self.advance() {
1958 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
1959 other => Err(ParseError {
1960 message: format!("expected identifier or string, got {other:?}"),
1961 token_pos: self.pos.saturating_sub(1),
1962 }),
1963 }
1964 }
1965
1966 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
1967 match self.advance() {
1968 Token::String(s) => Ok(s),
1969 other => Err(ParseError {
1970 message: format!("expected quoted string, got {other:?}"),
1971 token_pos: self.pos.saturating_sub(1),
1972 }),
1973 }
1974 }
1975
1976 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
1977 let mut head = self.parse_bare_select()?;
1982 while matches!(self.peek(), Token::Union) {
1983 self.advance();
1984 let kind = if matches!(self.peek(), Token::All) {
1985 self.advance();
1986 UnionKind::All
1987 } else {
1988 UnionKind::Distinct
1989 };
1990 let peer = self.parse_bare_select()?;
1991 head.unions.push((kind, peer));
1992 }
1993 head.order_by = if matches!(self.peek(), Token::Order) {
1994 self.advance();
1995 if !matches!(self.peek(), Token::By) {
1996 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
1997 }
1998 self.advance();
1999 let mut keys = Vec::new();
2002 loop {
2003 let expr = self.parse_expr(0)?;
2004 let desc = if matches!(self.peek(), Token::Desc) {
2005 self.advance();
2006 true
2007 } else if matches!(self.peek(), Token::Asc) {
2008 self.advance();
2009 false
2010 } else {
2011 false
2012 };
2013 keys.push(OrderBy { expr, desc });
2014 if matches!(self.peek(), Token::Comma) {
2015 self.advance();
2016 } else {
2017 break;
2018 }
2019 }
2020 keys
2021 } else {
2022 Vec::new()
2023 };
2024 head.limit = if matches!(self.peek(), Token::Limit) {
2025 self.advance();
2026 Some(self.parse_limit_expr("LIMIT")?)
2027 } else {
2028 None
2029 };
2030 head.offset = if matches!(self.peek(), Token::Offset) {
2031 self.advance();
2032 Some(self.parse_limit_expr("OFFSET")?)
2033 } else {
2034 None
2035 };
2036 Ok(Statement::Select(head))
2037 }
2038
2039 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
2044 match self.advance() {
2045 Token::Integer(n) if n >= 0 => u32::try_from(n)
2046 .map(crate::ast::LimitExpr::Literal)
2047 .map_err(|_| ParseError {
2048 message: alloc::format!("{label} value too large: {n}"),
2049 token_pos: self.pos.saturating_sub(1),
2050 }),
2051 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
2052 other => Err(ParseError {
2053 message: alloc::format!(
2054 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
2055 ),
2056 token_pos: self.pos.saturating_sub(1),
2057 }),
2058 }
2059 }
2060
2061 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
2066 if !matches!(self.peek(), Token::Select) {
2067 return Err(self.err(format!(
2068 "expected SELECT to start a query block, got {:?}",
2069 self.peek()
2070 )));
2071 }
2072 self.advance();
2073 let distinct = if matches!(self.peek(), Token::Distinct) {
2074 self.advance();
2075 true
2076 } else {
2077 false
2078 };
2079 let items = self.parse_select_list()?;
2080 let from = if matches!(self.peek(), Token::From) {
2081 self.advance();
2082 Some(self.parse_from_clause()?)
2083 } else {
2084 None
2085 };
2086 let where_ = if matches!(self.peek(), Token::Where) {
2087 self.advance();
2088 Some(self.parse_expr(0)?)
2089 } else {
2090 None
2091 };
2092 let mut group_by_all = false;
2093 let group_by = if matches!(self.peek(), Token::Group) {
2094 self.advance();
2095 if !matches!(self.peek(), Token::By) {
2096 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
2097 }
2098 self.advance();
2099 if matches!(self.peek(), Token::All) {
2102 self.advance();
2103 group_by_all = true;
2104 None
2105 } else {
2106 let mut groups = Vec::new();
2107 loop {
2108 groups.push(self.parse_expr(0)?);
2109 if matches!(self.peek(), Token::Comma) {
2110 self.advance();
2111 } else {
2112 break;
2113 }
2114 }
2115 Some(groups)
2116 }
2117 } else {
2118 None
2119 };
2120 let having = if matches!(self.peek(), Token::Having) {
2121 self.advance();
2122 Some(self.parse_expr(0)?)
2123 } else {
2124 None
2125 };
2126 Ok(SelectStatement {
2127 ctes: Vec::new(),
2128 distinct,
2129 items,
2130 from,
2131 where_,
2132 group_by,
2133 group_by_all,
2134 having,
2135 unions: Vec::new(),
2136 order_by: Vec::new(),
2137 limit: None,
2138 offset: None,
2139 })
2140 }
2141
2142 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
2143 debug_assert!(matches!(self.peek(), Token::Table));
2145 self.advance();
2146 let if_not_exists = self.consume_if_not_exists();
2147 let name = self.expect_ident_like()?;
2148 if !matches!(self.peek(), Token::LParen) {
2149 return Err(self.err(format!(
2150 "expected '(' after table name, got {:?}",
2151 self.peek()
2152 )));
2153 }
2154 self.advance();
2155 let mut columns = Vec::new();
2156 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
2157 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
2158 loop {
2159 if self.peek_table_level_pk_start() {
2165 table_constraints.push(self.parse_table_level_primary_key()?);
2166 } else if self.peek_table_level_unique_start() {
2167 table_constraints.push(self.parse_table_level_unique()?);
2168 } else if self.peek_table_level_check_start() {
2169 table_constraints.push(self.parse_table_level_check()?);
2171 } else if self.peek_constraint_or_fk_start() {
2172 foreign_keys.push(self.parse_table_level_fk()?);
2173 } else {
2174 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
2175 if col.is_unique {
2179 table_constraints.push(crate::ast::TableConstraint::Unique {
2180 name: None,
2181 columns: alloc::vec![col.name.clone()],
2182 nulls_not_distinct: false,
2183 });
2184 }
2185 if let Some(check_expr) = col.check.clone() {
2186 table_constraints.push(crate::ast::TableConstraint::Check {
2187 name: None,
2188 expr: check_expr,
2189 });
2190 }
2191 columns.push(col);
2192 if let Some(fk) = col_level_fk {
2193 foreign_keys.push(fk);
2194 }
2195 }
2196 match self.peek() {
2197 Token::Comma => {
2198 self.advance();
2199 }
2200 Token::RParen => {
2201 self.advance();
2202 break;
2203 }
2204 other => {
2205 return Err(
2206 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
2207 );
2208 }
2209 }
2210 }
2211 if columns.is_empty() {
2212 return Err(self.err("CREATE TABLE requires at least one column".into()));
2213 }
2214 Ok(Statement::CreateTable(CreateTableStatement {
2215 name,
2216 columns,
2217 if_not_exists,
2218 foreign_keys,
2219 table_constraints,
2220 }))
2221 }
2222
2223 fn peek_table_level_pk_start(&self) -> bool {
2228 let cur = self.peek();
2229 let nxt = self.tokens.get(self.pos + 1);
2230 let nxt2 = self.tokens.get(self.pos + 2);
2231 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
2232 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
2233 let is_lparen = matches!(nxt2, Some(Token::LParen));
2234 is_primary && is_key && is_lparen
2235 }
2236
2237 fn peek_table_level_unique_start(&self) -> bool {
2241 let cur = self.peek();
2242 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
2243 if !is_unique {
2244 return false;
2245 }
2246 let n1 = self.tokens.get(self.pos + 1);
2247 if matches!(n1, Some(Token::LParen)) {
2249 return true;
2250 }
2251 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
2253 if !is_nulls {
2254 return false;
2255 }
2256 let n2 = self.tokens.get(self.pos + 2);
2257 let n3 = self.tokens.get(self.pos + 3);
2258 let n4 = self.tokens.get(self.pos + 4);
2259 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
2261 return true;
2262 }
2263 if matches!(n2, Some(Token::Not))
2265 && matches!(n3, Some(Token::Distinct))
2266 && matches!(n4, Some(Token::LParen))
2267 {
2268 return true;
2269 }
2270 false
2271 }
2272
2273 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2274 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
2277 Ok(crate::ast::TableConstraint::PrimaryKey {
2278 name: None,
2279 columns,
2280 })
2281 }
2282
2283 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2284 self.advance(); let mut nulls_not_distinct = false;
2289 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
2290 let n1 = self.tokens.get(self.pos + 1);
2291 let n2 = self.tokens.get(self.pos + 2);
2292 let is_not = matches!(n1, Some(Token::Not));
2293 let is_distinct = matches!(n2, Some(Token::Distinct));
2294 if is_not && is_distinct {
2295 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
2299 } else if matches!(n1, Some(Token::Distinct)) {
2300 self.advance(); self.advance(); }
2303 }
2304 let columns = self.parse_paren_ident_list("UNIQUE")?;
2305 Ok(crate::ast::TableConstraint::Unique {
2306 name: None,
2307 columns,
2308 nulls_not_distinct,
2309 })
2310 }
2311
2312 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
2316 self.advance(); if !matches!(self.peek(), Token::LParen) {
2318 return Err(self.err(alloc::format!(
2319 "expected '(' after CHECK, got {:?}",
2320 self.peek()
2321 )));
2322 }
2323 self.advance();
2324 let expr = self.parse_expr(0)?;
2325 if !matches!(self.peek(), Token::RParen) {
2326 return Err(self.err(alloc::format!(
2327 "expected ')' to close CHECK predicate, got {:?}",
2328 self.peek()
2329 )));
2330 }
2331 self.advance();
2332 Ok(crate::ast::TableConstraint::Check { name: None, expr })
2333 }
2334
2335 fn peek_table_level_check_start(&self) -> bool {
2337 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
2338 }
2339
2340 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
2341 if !matches!(self.peek(), Token::LParen) {
2342 return Err(self.err(alloc::format!(
2343 "expected '(' after {ctx}, got {:?}",
2344 self.peek()
2345 )));
2346 }
2347 self.advance();
2348 let mut out = Vec::new();
2349 loop {
2350 out.push(self.expect_ident_like()?);
2351 match self.peek() {
2352 Token::Comma => {
2353 self.advance();
2354 }
2355 Token::RParen => {
2356 self.advance();
2357 break;
2358 }
2359 other => {
2360 return Err(self.err(alloc::format!(
2361 "expected ',' or ')' in {ctx} list, got {other:?}"
2362 )));
2363 }
2364 }
2365 }
2366 if out.is_empty() {
2367 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
2368 }
2369 Ok(out)
2370 }
2371
2372 fn peek_constraint_or_fk_start(&self) -> bool {
2377 let is_constraint_kw = matches!(
2378 self.peek(),
2379 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
2380 );
2381 let is_foreign_kw = matches!(
2382 self.peek(),
2383 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
2384 );
2385 is_constraint_kw || is_foreign_kw
2386 }
2387
2388 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
2392 let mut name: Option<String> = None;
2393 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
2394 self.advance();
2395 name = Some(self.expect_ident_like()?);
2396 }
2397 match self.advance() {
2399 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
2400 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
2401 }
2402 match self.advance() {
2404 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
2405 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
2406 }
2407 if !matches!(self.peek(), Token::LParen) {
2409 return Err(self.err(format!(
2410 "expected '(' after FOREIGN KEY, got {:?}",
2411 self.peek()
2412 )));
2413 }
2414 self.advance();
2415 let mut columns = Vec::new();
2416 loop {
2417 columns.push(self.expect_ident_like()?);
2418 match self.peek() {
2419 Token::Comma => {
2420 self.advance();
2421 }
2422 Token::RParen => {
2423 self.advance();
2424 break;
2425 }
2426 other => {
2427 return Err(self.err(format!(
2428 "expected ',' or ')' in FK column list, got {other:?}"
2429 )));
2430 }
2431 }
2432 }
2433 if columns.is_empty() {
2434 return Err(self.err("FOREIGN KEY requires at least one column".into()));
2435 }
2436 let (parent_table, parent_columns, on_delete, on_update) =
2437 self.parse_references_tail(columns.len())?;
2438 Ok(ForeignKeyConstraint {
2439 name,
2440 columns,
2441 parent_table,
2442 parent_columns,
2443 on_delete,
2444 on_update,
2445 })
2446 }
2447
2448 fn parse_references_tail(
2453 &mut self,
2454 expected_arity: usize,
2455 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
2456 match self.advance() {
2457 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
2458 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
2459 }
2460 let parent_table = self.expect_ident_like()?;
2461 let mut parent_columns: Vec<String> = Vec::new();
2462 if matches!(self.peek(), Token::LParen) {
2463 self.advance();
2464 loop {
2465 parent_columns.push(self.expect_ident_like()?);
2466 match self.peek() {
2467 Token::Comma => {
2468 self.advance();
2469 }
2470 Token::RParen => {
2471 self.advance();
2472 break;
2473 }
2474 other => {
2475 return Err(self.err(format!(
2476 "expected ',' or ')' in REFERENCES column list, got {other:?}"
2477 )));
2478 }
2479 }
2480 }
2481 }
2482 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
2483 return Err(self.err(format!(
2484 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
2485 expected_arity,
2486 parent_columns.len()
2487 )));
2488 }
2489 loop {
2495 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2496 return Err(self.err(
2497 "DEFERRABLE constraints are not supported (SPG is single-writer; \
2498 constraints are always evaluated immediately at commit)"
2499 .into(),
2500 ));
2501 }
2502 if matches!(self.peek(), Token::Not) {
2503 let look = self.tokens.get(self.pos + 1);
2504 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2505 self.advance();
2508 self.advance();
2509 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially"))
2511 {
2512 self.advance();
2513 match self.advance() {
2514 Token::Ident(s) if s.eq_ignore_ascii_case("immediate") => {}
2515 other => {
2516 return Err(self.err(format!(
2517 "expected IMMEDIATE after INITIALLY for NOT DEFERRABLE, \
2518 got {other:?}"
2519 )));
2520 }
2521 }
2522 }
2523 continue;
2524 }
2525 break;
2526 }
2527 break;
2528 }
2529 let mut on_delete = FkAction::Restrict;
2532 let mut on_update = FkAction::Restrict;
2533 let mut seen_on_delete = false;
2534 let mut seen_on_update = false;
2535 loop {
2536 if !matches!(self.peek(), Token::On) {
2537 break;
2538 }
2539 self.advance();
2540 let which = self.advance();
2541 let action = self.parse_fk_action()?;
2542 match which {
2543 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
2544 if seen_on_delete {
2545 return Err(self.err("ON DELETE specified twice".into()));
2546 }
2547 seen_on_delete = true;
2548 on_delete = action;
2549 }
2550 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
2551 if seen_on_update {
2552 return Err(self.err("ON UPDATE specified twice".into()));
2553 }
2554 seen_on_update = true;
2555 on_update = action;
2556 }
2557 other => {
2558 return Err(
2559 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
2560 );
2561 }
2562 }
2563 }
2564 Ok((parent_table, parent_columns, on_delete, on_update))
2565 }
2566
2567 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
2570 match self.advance() {
2571 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
2572 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
2573 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
2574 Token::Null => Ok(FkAction::SetNull),
2575 Token::Default => Ok(FkAction::SetDefault),
2576 other => Err(self.err(format!(
2577 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
2578 ))),
2579 },
2580 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
2581 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
2582 other => Err(self.err(format!(
2583 "expected ACTION after NO in FK action, got {other:?}"
2584 ))),
2585 },
2586 other => Err(self.err(format!(
2587 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
2588 ))),
2589 }
2590 }
2591
2592 fn consume_if_not_exists(&mut self) -> bool {
2595 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2599 if !looks_like_if {
2600 return false;
2601 }
2602 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
2605 return false;
2606 }
2607 if !matches!(
2608 self.tokens.get(self.pos + 2),
2609 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2610 ) {
2611 return false;
2612 }
2613 self.advance(); self.advance(); self.advance(); true
2617 }
2618
2619 fn consume_if_exists(&mut self) -> bool {
2623 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
2624 if !looks_like_if {
2625 return false;
2626 }
2627 if !matches!(
2628 self.tokens.get(self.pos + 1),
2629 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
2630 ) {
2631 return false;
2632 }
2633 self.advance(); self.advance(); true
2636 }
2637
2638 fn consume_optional_index_column_qualifiers(&mut self) {
2644 loop {
2645 match self.peek() {
2646 Token::Asc | Token::Desc => {
2647 self.advance();
2648 }
2649 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
2650 let look = self.tokens.get(self.pos + 1);
2651 if matches!(
2652 look,
2653 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
2654 || k.eq_ignore_ascii_case("last")
2655 ) {
2656 self.advance();
2657 self.advance();
2658 } else {
2659 break;
2660 }
2661 }
2662 _ => break,
2663 }
2664 }
2665 }
2666
2667 fn parse_create_index_stmt_after_create(
2668 &mut self,
2669 is_unique: bool,
2670 ) -> Result<Statement, ParseError> {
2671 debug_assert!(matches!(self.peek(), Token::Index));
2673 self.advance();
2674 let if_not_exists = self.consume_if_not_exists();
2675 let name = self.expect_ident_like()?;
2676 if !matches!(self.peek(), Token::On) {
2677 return Err(self.err(format!(
2678 "expected ON after CREATE INDEX <name>, got {:?}",
2679 self.peek()
2680 )));
2681 }
2682 self.advance();
2683 let table = self.expect_ident_like()?;
2684 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
2689 self.advance();
2690 let m = self.expect_ident_like()?;
2691 match m.to_ascii_lowercase().as_str() {
2692 "hnsw" => IndexMethod::Hnsw,
2693 "btree" => IndexMethod::BTree,
2694 "brin" => IndexMethod::Brin,
2695 "gin" => IndexMethod::Gin,
2700 "gist" | "spgist" | "hash" => IndexMethod::BTree,
2709 "ivfflat" => IndexMethod::Hnsw,
2717 other => {
2718 return Err(self.err(alloc::format!(
2719 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
2720 )));
2721 }
2722 }
2723 } else {
2724 IndexMethod::BTree
2725 };
2726 if !matches!(self.peek(), Token::LParen) {
2727 return Err(self.err(format!(
2728 "expected '(' before indexed column, got {:?}",
2729 self.peek()
2730 )));
2731 }
2732 self.advance();
2733 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
2742 Token::Ident(s) | Token::QuotedIdent(s)
2749 if matches!(
2750 self.tokens.get(self.pos + 1),
2751 Some(Token::RParen | Token::Comma)
2752 ) =>
2753 {
2754 self.advance();
2755 (s, None)
2756 }
2757 Token::Ident(s) | Token::QuotedIdent(s)
2765 if matches!(
2766 self.tokens.get(self.pos + 1),
2767 Some(Token::Ident(op) | Token::QuotedIdent(op))
2768 if is_vector_opclass_name(op)
2769 ) =>
2770 {
2771 self.advance(); self.advance(); (s, None)
2774 }
2775 Token::Ident(_) | Token::QuotedIdent(_) => {
2776 let key_expr = self.parse_expr(0)?;
2777 let primary = extract_first_column(&key_expr).ok_or_else(|| {
2778 self.err("expression index key must reference at least one column".into())
2779 })?;
2780 (primary, Some(key_expr))
2781 }
2782 other => {
2783 return Err(self.err(format!(
2784 "expected column ident or expression, got {other:?}"
2785 )));
2786 }
2787 };
2788 let mut extra_columns: Vec<String> = Vec::new();
2797 self.consume_optional_index_column_qualifiers();
2799 while matches!(self.peek(), Token::Comma) {
2800 self.advance();
2801 let extra = self.expect_ident_like()?;
2802 self.consume_optional_index_column_qualifiers();
2803 extra_columns.push(extra);
2804 }
2805 if !matches!(self.peek(), Token::RParen) {
2806 return Err(self.err(format!(
2807 "expected ')' after indexed column / expression, got {:?}",
2808 self.peek()
2809 )));
2810 }
2811 self.advance();
2812 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
2816 {
2817 self.advance();
2818 if !matches!(self.peek(), Token::LParen) {
2819 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
2820 }
2821 self.advance();
2822 let mut cols = Vec::new();
2823 loop {
2824 cols.push(self.expect_ident_like()?);
2825 match self.peek() {
2826 Token::Comma => {
2827 self.advance();
2828 }
2829 Token::RParen => {
2830 self.advance();
2831 break;
2832 }
2833 other => {
2834 return Err(self.err(format!(
2835 "expected ',' or ')' in INCLUDE list, got {other:?}"
2836 )));
2837 }
2838 }
2839 }
2840 cols
2841 } else {
2842 Vec::new()
2843 };
2844 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
2850 self.advance();
2851 if !matches!(self.peek(), Token::LParen) {
2852 return Err(self.err(format!(
2853 "expected '(' after WITH in CREATE INDEX, got {:?}",
2854 self.peek()
2855 )));
2856 }
2857 self.advance();
2858 loop {
2859 if matches!(self.peek(), Token::RParen) {
2860 self.advance();
2861 break;
2862 }
2863 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
2866 self.advance();
2867 let _ = self.advance(); }
2869 match self.peek() {
2870 Token::Comma => {
2871 self.advance();
2872 }
2873 Token::RParen => {
2874 self.advance();
2875 break;
2876 }
2877 other => {
2878 return Err(self.err(format!(
2879 "expected ',' or ')' in WITH (…) clause, got {other:?}"
2880 )));
2881 }
2882 }
2883 }
2884 }
2885 let partial_predicate = if matches!(self.peek(), Token::Where) {
2887 self.advance();
2888 Some(self.parse_expr(0)?)
2889 } else {
2890 None
2891 };
2892 if is_unique && !matches!(method, IndexMethod::BTree) {
2897 return Err(self.err(alloc::format!(
2898 "UNIQUE is only supported on BTree indexes, got USING {:?}",
2899 method
2900 )));
2901 }
2902 Ok(Statement::CreateIndex(CreateIndexStatement {
2903 name,
2904 table,
2905 column,
2906 method,
2907 if_not_exists,
2908 included_columns,
2909 partial_predicate,
2910 extra_columns: extra_columns.clone(),
2911 expression,
2912 is_unique,
2913 }))
2914 }
2915
2916 fn parse_column_def_with_fk(
2921 &mut self,
2922 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
2923 let col = self.parse_column_def()?;
2924 let inline_references = matches!(
2926 self.peek(),
2927 Token::Ident(s) if s.eq_ignore_ascii_case("references")
2928 );
2929 if !inline_references {
2930 return Ok((col, None));
2931 }
2932 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
2933 let fk = ForeignKeyConstraint {
2934 name: None,
2935 columns: vec![col.name.clone()],
2936 parent_table,
2937 parent_columns,
2938 on_delete,
2939 on_update,
2940 };
2941 Ok((col, Some(fk)))
2942 }
2943
2944 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
2952 let (ty, _, _) = self.parse_type_with_implied_flags()?;
2953 Ok(ty)
2954 }
2955
2956 fn parse_type_with_implied_flags(
2957 &mut self,
2958 ) -> Result<(ColumnTypeName, bool, bool), ParseError> {
2959 let ty_ident = match self.advance() {
2960 Token::Ident(s) => s,
2961 other => {
2962 return Err(ParseError {
2963 message: format!("expected column type, got {other:?}"),
2964 token_pos: self.pos.saturating_sub(1),
2965 });
2966 }
2967 };
2968 let mut implied_auto_increment = false;
2969 let mut implied_not_null = false;
2970 let mut ty = match ty_ident.as_str() {
2971 "smallserial" | "serial2" => {
2973 implied_auto_increment = true;
2974 implied_not_null = true;
2975 ColumnTypeName::SmallInt
2976 }
2977 "serial" | "serial4" => {
2978 implied_auto_increment = true;
2979 implied_not_null = true;
2980 ColumnTypeName::Int
2981 }
2982 "bigserial" | "serial8" => {
2983 implied_auto_increment = true;
2984 implied_not_null = true;
2985 ColumnTypeName::BigInt
2986 }
2987 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
2993 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
2995 "bigint" => ColumnTypeName::BigInt,
2996 "float" | "double" | "real" => {
3001 if ty_ident.eq_ignore_ascii_case("double")
3002 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
3003 {
3004 self.advance();
3005 }
3006 ColumnTypeName::Float
3007 }
3008 "float4" | "float8" => ColumnTypeName::Float,
3010 "text" => ColumnTypeName::Text,
3011 "bool" | "boolean" => ColumnTypeName::Bool,
3012 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
3013 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
3014 "vector" => {
3015 let dim = self.parse_paren_size("VECTOR")?;
3016 let encoding = self.parse_optional_vector_encoding()?;
3017 ColumnTypeName::Vector { dim, encoding }
3018 }
3019 "numeric" => {
3020 let (precision, scale) = self.parse_optional_numeric_params()?;
3021 ColumnTypeName::Numeric(precision, scale)
3022 }
3023 "date" => ColumnTypeName::Date,
3024 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
3027 "timestamptz" => ColumnTypeName::Timestamptz,
3031 "json" => ColumnTypeName::Json,
3036 "jsonb" => ColumnTypeName::Jsonb,
3037 "bytea" | "bytes" => ColumnTypeName::Bytes,
3043 "tsvector" => ColumnTypeName::TsVector,
3048 "tsquery" => ColumnTypeName::TsQuery,
3049 other => {
3050 return Err(ParseError {
3051 message: format!("unsupported column type {other:?}"),
3052 token_pos: self.pos.saturating_sub(1),
3053 });
3054 }
3055 };
3056 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned")) {
3061 self.advance();
3062 }
3063 if matches!(self.peek(), Token::LBracket) {
3068 self.advance();
3069 if !matches!(self.peek(), Token::RBracket) {
3070 return Err(self.err(alloc::format!(
3071 "TEXT[] takes no dimension; got {:?}",
3072 self.peek()
3073 )));
3074 }
3075 self.advance();
3076 ty = match ty {
3080 ColumnTypeName::Text => ColumnTypeName::TextArray,
3081 ColumnTypeName::Int => ColumnTypeName::IntArray,
3082 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
3083 other => {
3084 return Err(self.err(alloc::format!(
3085 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
3086 )));
3087 }
3088 };
3089 }
3090 Ok((ty, implied_auto_increment, implied_not_null))
3091 }
3092
3093 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
3094 let name = self.expect_ident_like()?;
3095 let (ty, implied_auto_increment, implied_not_null) =
3096 self.parse_type_with_implied_flags()?;
3097 let mut default: Option<Expr> = None;
3101 let mut nullable = !implied_not_null;
3102 let mut nullability_seen = implied_not_null;
3103 let mut auto_increment = implied_auto_increment;
3104 let mut is_primary_key = false;
3105 let mut is_unique = false;
3106 let mut check: Option<Expr> = None;
3107 loop {
3108 if matches!(self.peek(), Token::Default) {
3109 if default.is_some() {
3110 return Err(self.err("DEFAULT specified twice".into()));
3111 }
3112 self.advance();
3113 default = Some(self.parse_expr(0)?);
3114 continue;
3115 }
3116 if matches!(self.peek(), Token::Not) {
3117 if nullability_seen {
3118 return Err(self.err("NOT NULL specified twice".into()));
3119 }
3120 self.advance();
3121 if !matches!(self.peek(), Token::Null) {
3122 return Err(self.err(format!(
3123 "expected NULL after NOT in column def, got {:?}",
3124 self.peek()
3125 )));
3126 }
3127 self.advance();
3128 nullable = false;
3129 nullability_seen = true;
3130 continue;
3131 }
3132 if let Token::Ident(s) = self.peek()
3135 && (s.eq_ignore_ascii_case("auto_increment")
3136 || s.eq_ignore_ascii_case("autoincrement"))
3137 {
3138 if auto_increment {
3139 return Err(self.err("AUTO_INCREMENT specified twice".into()));
3140 }
3141 self.advance();
3142 auto_increment = true;
3143 continue;
3144 }
3145 if let Token::Ident(s) = self.peek()
3150 && s.eq_ignore_ascii_case("primary")
3151 {
3152 if is_primary_key {
3153 return Err(self.err("PRIMARY KEY specified twice".into()));
3154 }
3155 let next = self.tokens.get(self.pos + 1);
3157 let next_is_key = matches!(
3158 next,
3159 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
3160 );
3161 if !next_is_key {
3162 return Err(self.err(format!(
3163 "expected KEY after PRIMARY in column def, got {:?}",
3164 next
3165 )));
3166 }
3167 self.advance(); self.advance(); is_primary_key = true;
3170 if nullability_seen && nullable {
3171 return Err(self.err(
3172 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
3173 ));
3174 }
3175 nullable = false;
3176 nullability_seen = true;
3177 continue;
3178 }
3179 if let Token::Ident(s) = self.peek()
3183 && s.eq_ignore_ascii_case("unique")
3184 {
3185 if is_unique {
3186 return Err(self.err("UNIQUE specified twice".into()));
3187 }
3188 self.advance();
3189 is_unique = true;
3190 continue;
3191 }
3192 if let Token::Ident(s) = self.peek()
3197 && s.eq_ignore_ascii_case("check")
3198 {
3199 self.advance();
3200 if !matches!(self.peek(), Token::LParen) {
3201 return Err(self.err(alloc::format!(
3202 "expected '(' after CHECK in column def, got {:?}",
3203 self.peek()
3204 )));
3205 }
3206 self.advance();
3207 let pred = self.parse_expr(0)?;
3208 if !matches!(self.peek(), Token::RParen) {
3209 return Err(self.err(alloc::format!(
3210 "expected ')' to close CHECK predicate, got {:?}",
3211 self.peek()
3212 )));
3213 }
3214 self.advance();
3215 check = Some(match check.take() {
3216 Some(prev) => Expr::Binary {
3217 op: BinOp::And,
3218 lhs: Box::new(prev),
3219 rhs: Box::new(pred),
3220 },
3221 None => pred,
3222 });
3223 continue;
3224 }
3225 break;
3226 }
3227 Ok(ColumnDef {
3228 name,
3229 ty,
3230 nullable,
3231 default,
3232 auto_increment,
3233 is_primary_key,
3234 is_unique,
3235 check,
3236 })
3237 }
3238
3239 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
3243 if !matches!(self.peek(), Token::LParen) {
3244 return Ok((0, 0));
3248 }
3249 self.advance();
3250 let precision = match self.advance() {
3251 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
3252 other => {
3253 return Err(ParseError {
3254 message: format!(
3255 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
3256 ),
3257 token_pos: self.pos.saturating_sub(1),
3258 });
3259 }
3260 };
3261 let scale = if matches!(self.peek(), Token::Comma) {
3262 self.advance();
3263 match self.advance() {
3264 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
3265 u8::try_from(n).expect("range-checked")
3266 }
3267 other => {
3268 return Err(ParseError {
3269 message: format!(
3270 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
3271 ),
3272 token_pos: self.pos.saturating_sub(1),
3273 });
3274 }
3275 }
3276 } else {
3277 0
3278 };
3279 if !matches!(self.peek(), Token::RParen) {
3280 return Err(self.err(format!(
3281 "expected ')' to close NUMERIC params, got {:?}",
3282 self.peek()
3283 )));
3284 }
3285 self.advance();
3286 Ok((precision, scale))
3287 }
3288
3289 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
3297 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
3298 return Ok(VecEncoding::F32);
3299 }
3300 let n1 = self.tokens.get(self.pos + 1);
3306 let next_is_encoding = matches!(
3307 n1,
3308 Some(Token::Ident(s))
3309 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
3310 );
3311 if !next_is_encoding {
3312 return Ok(VecEncoding::F32);
3313 }
3314 self.advance();
3315 let enc_ident = match self.advance() {
3316 Token::Ident(s) => s,
3317 other => {
3318 return Err(self.err(format!(
3319 "expected vector encoding after USING, got {other:?}"
3320 )));
3321 }
3322 };
3323 match enc_ident.to_ascii_lowercase().as_str() {
3324 "sq8" => Ok(VecEncoding::Sq8),
3325 "half" => Ok(VecEncoding::F16),
3328 other => Err(self.err(format!(
3329 "unknown vector encoding {other:?}; supported: SQ8, HALF"
3330 ))),
3331 }
3332 }
3333
3334 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
3335 if !matches!(self.peek(), Token::LParen) {
3336 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
3337 }
3338 self.advance();
3339 let n = match self.advance() {
3340 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
3341 message: format!("{label} size too large: {n}"),
3342 token_pos: self.pos.saturating_sub(1),
3343 })?,
3344 other => {
3345 return Err(ParseError {
3346 message: format!("expected positive integer {label} size, got {other:?}"),
3347 token_pos: self.pos.saturating_sub(1),
3348 });
3349 }
3350 };
3351 if !matches!(self.peek(), Token::RParen) {
3352 return Err(self.err(format!(
3353 "expected ')' after {label} size, got {:?}",
3354 self.peek()
3355 )));
3356 }
3357 self.advance();
3358 Ok(n)
3359 }
3360
3361 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
3362 debug_assert!(matches!(self.peek(), Token::Insert));
3363 self.advance();
3364 if !matches!(self.peek(), Token::Into) {
3365 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
3366 }
3367 self.advance();
3368 let table = self.expect_ident_like()?;
3369 let columns = if matches!(self.peek(), Token::LParen) {
3371 self.advance();
3372 let mut names = Vec::new();
3373 loop {
3374 names.push(self.expect_ident_like()?);
3375 match self.peek() {
3376 Token::Comma => {
3377 self.advance();
3378 }
3379 Token::RParen => {
3380 self.advance();
3381 break;
3382 }
3383 other => {
3384 return Err(self.err(format!(
3385 "expected ',' or ')' in INSERT column list, got {other:?}"
3386 )));
3387 }
3388 }
3389 }
3390 Some(names)
3391 } else {
3392 None
3393 };
3394 if matches!(self.peek(), Token::Select) {
3397 let select_stmt = match self.parse_select_stmt()? {
3398 Statement::Select(s) => s,
3399 other => {
3400 return Err(self.err(alloc::format!(
3401 "expected SELECT after INSERT INTO ... target, got {other:?}"
3402 )));
3403 }
3404 };
3405 let on_conflict = self.parse_optional_on_conflict()?;
3406 let returning = self.parse_optional_returning()?;
3407 return Ok(Statement::Insert(InsertStatement {
3408 table,
3409 columns,
3410 rows: Vec::new(),
3411 select_source: Some(Box::new(select_stmt)),
3412 on_conflict,
3413 returning,
3414 }));
3415 }
3416 if !matches!(self.peek(), Token::Values) {
3417 return Err(self.err(format!(
3418 "expected VALUES or SELECT after table name, got {:?}",
3419 self.peek()
3420 )));
3421 }
3422 self.advance();
3423 if !matches!(self.peek(), Token::LParen) {
3424 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
3425 }
3426 let mut rows = Vec::new();
3427 loop {
3428 if !matches!(self.peek(), Token::LParen) {
3430 return Err(self.err(format!(
3431 "expected '(' for next VALUES tuple, got {:?}",
3432 self.peek()
3433 )));
3434 }
3435 self.advance();
3436 let mut tuple = Vec::new();
3437 loop {
3438 tuple.push(self.parse_expr(0)?);
3439 match self.peek() {
3440 Token::Comma => {
3441 self.advance();
3442 }
3443 Token::RParen => {
3444 self.advance();
3445 break;
3446 }
3447 other => {
3448 return Err(self.err(format!(
3449 "expected ',' or ')' in VALUES tuple, got {other:?}"
3450 )));
3451 }
3452 }
3453 }
3454 if tuple.is_empty() {
3455 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
3456 }
3457 rows.push(tuple);
3458 if matches!(self.peek(), Token::Comma) {
3460 self.advance();
3461 } else {
3462 break;
3463 }
3464 }
3465 let on_conflict = self.parse_optional_on_conflict()?;
3466 let returning = self.parse_optional_returning()?;
3467 Ok(Statement::Insert(InsertStatement {
3468 table,
3469 columns,
3470 rows,
3471 select_source: None,
3472 on_conflict,
3473 returning,
3474 }))
3475 }
3476
3477 fn parse_optional_on_conflict(
3482 &mut self,
3483 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
3484 if !matches!(self.peek(), Token::On) {
3485 return Ok(None);
3486 }
3487 let next_is_conflict = matches!(
3490 self.tokens.get(self.pos + 1),
3491 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
3492 );
3493 if !next_is_conflict {
3494 return Ok(None);
3495 }
3496 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
3500 if matches!(self.peek(), Token::LParen) {
3501 self.advance();
3502 loop {
3503 target_columns.push(self.expect_ident_like()?);
3504 match self.peek() {
3505 Token::Comma => {
3506 self.advance();
3507 }
3508 Token::RParen => {
3509 self.advance();
3510 break;
3511 }
3512 other => {
3513 return Err(self.err(alloc::format!(
3514 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
3515 )));
3516 }
3517 }
3518 }
3519 }
3520 match self.advance() {
3522 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
3523 other => {
3524 return Err(self.err(alloc::format!(
3525 "expected DO after ON CONFLICT [(…)], got {other:?}"
3526 )));
3527 }
3528 }
3529 let action = match self.advance() {
3531 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
3532 crate::ast::OnConflictAction::Nothing
3533 }
3534 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3535 self.parse_on_conflict_update_action()?
3536 }
3537 other => {
3538 return Err(self.err(alloc::format!(
3539 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
3540 )));
3541 }
3542 };
3543 Ok(Some(crate::ast::OnConflictClause {
3544 target_columns,
3545 action,
3546 }))
3547 }
3548
3549 fn parse_on_conflict_update_action(
3553 &mut self,
3554 ) -> Result<crate::ast::OnConflictAction, ParseError> {
3555 match self.advance() {
3557 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
3558 other => {
3559 return Err(self.err(alloc::format!(
3560 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
3561 )));
3562 }
3563 }
3564 let mut assignments: Vec<(String, Expr)> = Vec::new();
3565 loop {
3566 let col = self.expect_ident_like()?;
3567 if !matches!(self.peek(), Token::Eq) {
3568 return Err(self.err(alloc::format!(
3569 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
3570 self.peek()
3571 )));
3572 }
3573 self.advance();
3574 let value = self.parse_expr(0)?;
3575 assignments.push((col, value));
3576 if matches!(self.peek(), Token::Comma) {
3577 self.advance();
3578 continue;
3579 }
3580 break;
3581 }
3582 let where_ = if matches!(self.peek(), Token::Where) {
3583 self.advance();
3584 Some(self.parse_expr(0)?)
3585 } else {
3586 None
3587 };
3588 Ok(crate::ast::OnConflictAction::Update {
3589 assignments,
3590 where_,
3591 })
3592 }
3593
3594 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
3595 let mut items = Vec::new();
3596 loop {
3597 items.push(self.parse_select_item()?);
3598 if matches!(self.peek(), Token::Comma) {
3599 self.advance();
3600 } else {
3601 break;
3602 }
3603 }
3604 Ok(items)
3605 }
3606
3607 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
3608 if matches!(self.peek(), Token::Star) {
3609 self.advance();
3610 return Ok(SelectItem::Wildcard);
3611 }
3612 let expr = self.parse_expr(0)?;
3613 let alias = self.parse_optional_alias();
3614 Ok(SelectItem::Expr { expr, alias })
3615 }
3616
3617 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
3618 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
3622 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
3623 {
3624 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
3627 if !matches!(self.peek(), Token::RParen) {
3628 return Err(self.err(alloc::format!(
3629 "expected ')' after unnest() argument, got {:?}",
3630 self.peek()
3631 )));
3632 }
3633 self.advance();
3634 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
3635 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
3636 return Ok(TableRef {
3637 name,
3638 alias: alias_ident,
3639 as_of_segment: None,
3640 unnest_expr: Some(Box::new(expr)),
3641 unnest_column_aliases,
3642 });
3643 }
3644 let name = self.expect_ident_like()?;
3645 let as_of_segment = if matches!(self.peek(), Token::As)
3651 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
3652 {
3653 self.advance(); self.advance(); let kw = match self.peek().clone() {
3656 Token::Ident(s) | Token::QuotedIdent(s) => s,
3657 other => {
3658 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
3659 }
3660 };
3661 if !kw.eq_ignore_ascii_case("segment") {
3662 return Err(self.err(format!(
3663 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
3664 )));
3665 }
3666 self.advance();
3667 let id = match self.advance() {
3670 Token::String(s) => s
3671 .parse::<u32>()
3672 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3673 Token::Integer(n) => u32::try_from(n)
3674 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
3675 other => {
3676 return Err(self.err(format!(
3677 "expected segment id literal after AS OF SEGMENT, got {other:?}"
3678 )));
3679 }
3680 };
3681 Some(id)
3682 } else {
3683 None
3684 };
3685 let alias = self.parse_optional_alias();
3686 Ok(TableRef {
3687 name,
3688 alias,
3689 as_of_segment,
3690 unnest_expr: None,
3691 unnest_column_aliases: Vec::new(),
3692 })
3693 }
3694
3695 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
3701 let alias = self.parse_optional_alias();
3702 if alias.is_none() {
3703 return (None, Vec::new());
3704 }
3705 let mut cols: Vec<String> = Vec::new();
3706 if matches!(self.peek(), Token::LParen) {
3707 self.advance();
3708 loop {
3709 match self.peek().clone() {
3710 Token::Ident(s) | Token::QuotedIdent(s) => {
3711 self.advance();
3712 cols.push(s);
3713 }
3714 _ => break,
3715 }
3716 if matches!(self.peek(), Token::Comma) {
3717 self.advance();
3718 continue;
3719 }
3720 break;
3721 }
3722 if matches!(self.peek(), Token::RParen) {
3723 self.advance();
3724 }
3725 }
3726 (alias, cols)
3727 }
3728
3729 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
3734 let primary = self.parse_table_ref()?;
3735 let mut joins = Vec::new();
3736 loop {
3737 if matches!(self.peek(), Token::Comma) {
3739 self.advance();
3740 let table = self.parse_table_ref()?;
3741 joins.push(FromJoin {
3742 kind: JoinKind::Cross,
3743 table,
3744 on: None,
3745 });
3746 continue;
3747 }
3748 let kind =
3751 match self.peek() {
3752 Token::Inner => {
3753 self.advance();
3754 if !matches!(self.peek(), Token::Join) {
3755 return Err(self
3756 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
3757 }
3758 self.advance();
3759 JoinKind::Inner
3760 }
3761 Token::Left => {
3762 self.advance();
3763 if matches!(self.peek(), Token::Outer) {
3764 self.advance();
3765 }
3766 if !matches!(self.peek(), Token::Join) {
3767 return Err(self.err(format!(
3768 "expected JOIN after LEFT [OUTER], got {:?}",
3769 self.peek()
3770 )));
3771 }
3772 self.advance();
3773 JoinKind::Left
3774 }
3775 Token::Cross => {
3776 self.advance();
3777 if !matches!(self.peek(), Token::Join) {
3778 return Err(self
3779 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
3780 }
3781 self.advance();
3782 JoinKind::Cross
3783 }
3784 Token::Join => {
3785 self.advance();
3786 JoinKind::Inner
3787 }
3788 _ => break,
3789 };
3790 let table = self.parse_table_ref()?;
3791 let on = if matches!(self.peek(), Token::On) {
3792 self.advance();
3793 Some(self.parse_expr(0)?)
3794 } else if kind == JoinKind::Cross {
3795 None
3796 } else {
3797 return Err(self.err(format!(
3798 "expected ON after {:?} JOIN, got {:?}",
3799 kind,
3800 self.peek()
3801 )));
3802 };
3803 joins.push(FromJoin { kind, table, on });
3804 }
3805 Ok(FromClause { primary, joins })
3806 }
3807
3808 fn parse_optional_alias(&mut self) -> Option<String> {
3813 if matches!(self.peek(), Token::As) {
3814 self.advance();
3815 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3820 return self.expect_ident_like().ok();
3821 }
3822 return None;
3823 }
3824 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
3825 return self.expect_ident_like().ok();
3826 }
3827 None
3828 }
3829
3830 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
3832 let mut lhs = self.parse_unary()?;
3833 while let Some((op, prec)) = binop_from(self.peek()) {
3834 if prec < min_prec {
3835 break;
3836 }
3837 self.advance();
3838 let any_kind = match self.peek() {
3843 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
3844 Some(false)
3845 }
3846 Token::Ident(s) | Token::QuotedIdent(s)
3847 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
3848 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
3849 {
3850 Some(s.eq_ignore_ascii_case("any"))
3851 }
3852 _ => None,
3853 };
3854 if let Some(is_any) = any_kind {
3855 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
3858 if !matches!(self.peek(), Token::RParen) {
3859 return Err(self.err(alloc::format!(
3860 "expected ')' after ANY/ALL argument, got {:?}",
3861 self.peek()
3862 )));
3863 }
3864 self.advance();
3865 lhs = Expr::AnyAll {
3866 expr: Box::new(lhs),
3867 op,
3868 array: Box::new(arr),
3869 is_any,
3870 };
3871 continue;
3872 }
3873 let rhs = self.parse_expr(prec + 1)?;
3874 lhs = Expr::Binary {
3875 lhs: Box::new(lhs),
3876 op,
3877 rhs: Box::new(rhs),
3878 };
3879 }
3880 Ok(lhs)
3881 }
3882
3883 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
3884 match self.peek() {
3885 Token::Not => {
3886 self.advance();
3887 let e = self.parse_expr(3)?;
3890 Ok(Expr::Unary {
3891 op: UnOp::Not,
3892 expr: Box::new(e),
3893 })
3894 }
3895 Token::Minus => {
3896 self.advance();
3897 let e = self.parse_expr(8)?;
3900 Ok(Expr::Unary {
3901 op: UnOp::Neg,
3902 expr: Box::new(e),
3903 })
3904 }
3905 _ => self.parse_atom(),
3906 }
3907 }
3908
3909 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
3910 let tok_pos = self.pos;
3911 match self.advance() {
3912 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
3913 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
3914 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
3915 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
3916 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
3917 Token::Null => Ok(Expr::Literal(Literal::Null)),
3918 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
3922 Token::LParen => {
3923 if matches!(self.peek(), Token::Select) {
3927 let inner = self.parse_select_stmt()?;
3928 match self.advance() {
3929 Token::RParen => {
3930 let Statement::Select(s) = inner else {
3931 unreachable!("parse_select_stmt returns Select")
3932 };
3933 Ok(Expr::ScalarSubquery(Box::new(s)))
3934 }
3935 other => Err(ParseError {
3936 message: format!("expected ')' after scalar subquery, got {other:?}"),
3937 token_pos: self.pos.saturating_sub(1),
3938 }),
3939 }
3940 } else {
3941 let e = self.parse_expr(0)?;
3942 match self.advance() {
3943 Token::RParen => Ok(e),
3944 other => Err(ParseError {
3945 message: format!("expected ')', got {other:?}"),
3946 token_pos: self.pos.saturating_sub(1),
3947 }),
3948 }
3949 }
3950 }
3951 Token::LBracket => self.parse_vector_literal_body(),
3952 Token::Extract => self.parse_extract_atom(),
3953 Token::Interval => self.parse_interval_atom(),
3954 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
3959 self.parse_exists_atom(false)
3960 }
3961 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
3965 self.parse_case_atom()
3966 }
3967 Token::Ident(s) | Token::QuotedIdent(s)
3971 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
3972 {
3973 self.advance(); let mut items: Vec<Expr> = Vec::new();
3975 if !matches!(self.peek(), Token::RBracket) {
3976 loop {
3977 items.push(self.parse_expr(0)?);
3978 match self.peek() {
3979 Token::Comma => {
3980 self.advance();
3981 }
3982 Token::RBracket => break,
3983 other => {
3984 return Err(self.err(alloc::format!(
3985 "expected ',' or ']' in ARRAY literal, got {other:?}"
3986 )));
3987 }
3988 }
3989 }
3990 }
3991 self.advance(); Ok(Expr::Array(items))
3993 }
3994 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
3995 other => Err(ParseError {
3996 message: format!("unexpected token {other:?} in expression"),
3997 token_pos: tok_pos,
3998 }),
3999 }
4000 .and_then(|atom| self.finish_postfix_casts(atom))
4002 }
4003
4004 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
4007 loop {
4008 if matches!(self.peek(), Token::DoubleColon) {
4009 self.advance();
4010 let target = match self.advance() {
4015 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
4016 "int" | "integer" | "int4" => {
4017 if matches!(self.peek(), Token::LBracket)
4018 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4019 {
4020 self.advance();
4021 self.advance();
4022 CastTarget::IntArray
4023 } else {
4024 CastTarget::Int
4025 }
4026 }
4027 "bigint" | "int8" => {
4028 if matches!(self.peek(), Token::LBracket)
4029 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4030 {
4031 self.advance();
4032 self.advance();
4033 CastTarget::BigIntArray
4034 } else {
4035 CastTarget::BigInt
4036 }
4037 }
4038 "float" | "double" | "real" => CastTarget::Float,
4039 "text" => {
4040 if matches!(self.peek(), Token::LBracket)
4042 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
4043 {
4044 self.advance();
4045 self.advance();
4046 CastTarget::TextArray
4047 } else {
4048 CastTarget::Text
4049 }
4050 }
4051 "bool" | "boolean" => CastTarget::Bool,
4052 "vector" => CastTarget::Vector,
4053 "date" => CastTarget::Date,
4054 "timestamp" | "datetime" => CastTarget::Timestamp,
4055 "timestamptz" => CastTarget::Timestamptz,
4056 "interval" => CastTarget::Interval,
4057 "json" => CastTarget::Json,
4058 "jsonb" => CastTarget::Jsonb,
4059 "regtype" => CastTarget::RegType,
4060 "regclass" => CastTarget::RegClass,
4061 "tsvector" => CastTarget::TsVector,
4065 "tsquery" => CastTarget::TsQuery,
4066 other => {
4067 return Err(ParseError {
4068 message: format!("unsupported cast target `::{other}`"),
4069 token_pos: self.pos.saturating_sub(1),
4070 });
4071 }
4072 },
4073 Token::Interval => CastTarget::Interval,
4074 other => {
4075 return Err(ParseError {
4076 message: format!("expected type ident after `::`, got {other:?}"),
4077 token_pos: self.pos.saturating_sub(1),
4078 });
4079 }
4080 };
4081 expr = Expr::Cast {
4082 expr: Box::new(expr),
4083 target,
4084 };
4085 continue;
4086 }
4087 if matches!(self.peek(), Token::Is) {
4088 self.advance();
4089 let negated = if matches!(self.peek(), Token::Not) {
4090 self.advance();
4091 true
4092 } else {
4093 false
4094 };
4095 if matches!(self.peek(), Token::Distinct) {
4098 self.advance();
4099 if !matches!(self.peek(), Token::From) {
4100 return Err(self.err(format!(
4101 "expected FROM after IS{} DISTINCT, got {:?}",
4102 if negated { " NOT" } else { "" },
4103 self.peek()
4104 )));
4105 }
4106 self.advance();
4107 let rhs = self.parse_expr(20)?;
4111 let op = if negated {
4112 BinOp::IsNotDistinctFrom
4113 } else {
4114 BinOp::IsDistinctFrom
4115 };
4116 expr = Expr::Binary {
4117 op,
4118 lhs: Box::new(expr),
4119 rhs: Box::new(rhs),
4120 };
4121 continue;
4122 }
4123 if !matches!(self.peek(), Token::Null) {
4124 return Err(self.err(format!(
4125 "expected NULL or DISTINCT after IS{}, got {:?}",
4126 if negated { " NOT" } else { "" },
4127 self.peek()
4128 )));
4129 }
4130 self.advance();
4131 expr = Expr::IsNull {
4132 expr: Box::new(expr),
4133 negated,
4134 };
4135 continue;
4136 }
4137 let negated = if matches!(self.peek(), Token::Not) {
4141 let next = self.tokens.get(self.pos + 1);
4142 matches!(next, Some(Token::Between | Token::In | Token::Like))
4143 } else {
4144 false
4145 };
4146 if negated {
4147 self.advance();
4148 }
4149 if matches!(self.peek(), Token::Between) {
4150 expr = self.parse_between_tail(expr, negated)?;
4151 continue;
4152 }
4153 if matches!(self.peek(), Token::In) {
4154 expr = self.parse_in_tail(expr, negated)?;
4155 continue;
4156 }
4157 if matches!(self.peek(), Token::Like) {
4158 self.advance();
4159 let pattern = self.parse_expr(5)?;
4162 expr = Expr::Like {
4163 expr: Box::new(expr),
4164 pattern: Box::new(pattern),
4165 negated,
4166 };
4167 continue;
4168 }
4169 if matches!(self.peek(), Token::LBracket) {
4173 self.advance();
4174 let index = self.parse_expr(0)?;
4175 if !matches!(self.peek(), Token::RBracket) {
4176 return Err(self.err(alloc::format!(
4177 "expected ']' after array index, got {:?}",
4178 self.peek()
4179 )));
4180 }
4181 self.advance();
4182 expr = Expr::ArraySubscript {
4183 target: Box::new(expr),
4184 index: Box::new(index),
4185 };
4186 continue;
4187 }
4188 return Ok(expr);
4189 }
4190 }
4191
4192 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4196 self.advance(); let low = self.parse_expr(5)?;
4198 if !matches!(self.peek(), Token::And) {
4199 return Err(self.err(format!(
4200 "expected AND after BETWEEN low bound, got {:?}",
4201 self.peek()
4202 )));
4203 }
4204 self.advance();
4205 let high = self.parse_expr(5)?;
4206 let target = Box::new(expr);
4207 let combined = Expr::Binary {
4208 lhs: Box::new(Expr::Binary {
4209 lhs: target.clone(),
4210 op: BinOp::GtEq,
4211 rhs: Box::new(low),
4212 }),
4213 op: BinOp::And,
4214 rhs: Box::new(Expr::Binary {
4215 lhs: target,
4216 op: BinOp::LtEq,
4217 rhs: Box::new(high),
4218 }),
4219 };
4220 Ok(maybe_not(combined, negated))
4221 }
4222
4223 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
4228 let mut recursive = false;
4233 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4234 && s.eq_ignore_ascii_case("recursive")
4235 {
4236 self.advance();
4237 recursive = true;
4238 }
4239 let mut ctes = Vec::new();
4240 loop {
4241 let name = self.expect_ident_like()?;
4242 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
4246 self.advance();
4247 let mut names = Vec::new();
4248 loop {
4249 names.push(self.expect_ident_like()?);
4250 if matches!(self.peek(), Token::Comma) {
4251 self.advance();
4252 continue;
4253 }
4254 break;
4255 }
4256 if !matches!(self.peek(), Token::RParen) {
4257 return Err(self.err(format!(
4258 "expected ')' to close CTE column list, got {:?}",
4259 self.peek()
4260 )));
4261 }
4262 self.advance();
4263 names
4264 } else {
4265 Vec::new()
4266 };
4267 if !matches!(self.peek(), Token::As) {
4271 return Err(self.err(format!(
4272 "expected AS after CTE name {name:?}, got {:?}",
4273 self.peek()
4274 )));
4275 }
4276 self.advance();
4277 if !matches!(self.peek(), Token::LParen) {
4278 return Err(self.err(format!(
4279 "expected '(' after AS in WITH clause, got {:?}",
4280 self.peek()
4281 )));
4282 }
4283 self.advance();
4284 if !matches!(self.peek(), Token::Select) {
4285 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
4286 }
4287 let inner = self.parse_select_stmt()?;
4288 if !matches!(self.peek(), Token::RParen) {
4289 return Err(self.err(format!(
4290 "expected ')' after CTE body, got {:?}",
4291 self.peek()
4292 )));
4293 }
4294 self.advance();
4295 let Statement::Select(body) = inner else {
4296 unreachable!("parse_select_stmt returns Select")
4297 };
4298 ctes.push(crate::ast::Cte {
4299 name,
4300 body,
4301 recursive,
4302 column_overrides,
4303 });
4304 if matches!(self.peek(), Token::Comma) {
4305 self.advance();
4306 continue;
4307 }
4308 break;
4309 }
4310 if !matches!(self.peek(), Token::Select) {
4312 return Err(self.err(format!(
4313 "expected SELECT after WITH clause, got {:?}",
4314 self.peek()
4315 )));
4316 }
4317 let body_stmt = self.parse_select_stmt()?;
4318 let Statement::Select(mut body) = body_stmt else {
4319 unreachable!()
4320 };
4321 body.ctes = ctes;
4322 Ok(Statement::Select(body))
4323 }
4324
4325 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
4334 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
4338 None
4339 } else {
4340 Some(Box::new(self.parse_expr(0)?))
4341 };
4342 let mut branches: Vec<(Expr, Expr)> = Vec::new();
4343 loop {
4344 match self.peek() {
4345 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
4346 self.advance();
4347 let cond = self.parse_expr(0)?;
4348 match self.peek() {
4349 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
4350 self.advance();
4351 }
4352 other => {
4353 return Err(self.err(alloc::format!(
4354 "expected THEN after CASE WHEN <expr>, got {other:?}"
4355 )));
4356 }
4357 }
4358 let value = self.parse_expr(0)?;
4359 branches.push((cond, value));
4360 }
4361 _ => break,
4362 }
4363 }
4364 if branches.is_empty() {
4365 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
4366 }
4367 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
4368 {
4369 self.advance();
4370 Some(Box::new(self.parse_expr(0)?))
4371 } else {
4372 None
4373 };
4374 match self.peek() {
4375 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
4376 self.advance();
4377 }
4378 other => {
4379 return Err(self.err(alloc::format!(
4380 "expected END to close CASE expression, got {other:?}"
4381 )));
4382 }
4383 }
4384 Ok(Expr::Case {
4385 operand,
4386 branches,
4387 else_branch,
4388 })
4389 }
4390
4391 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
4392 if !matches!(self.peek(), Token::LParen) {
4393 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
4394 }
4395 self.advance();
4396 let inner = self.parse_select_stmt()?;
4397 if !matches!(self.peek(), Token::RParen) {
4398 return Err(self.err(format!(
4399 "expected ')' after EXISTS-subquery, got {:?}",
4400 self.peek()
4401 )));
4402 }
4403 self.advance();
4404 let Statement::Select(s) = inner else {
4405 unreachable!("parse_select_stmt returns Select")
4406 };
4407 Ok(Expr::Exists {
4408 subquery: Box::new(s),
4409 negated,
4410 })
4411 }
4412
4413 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
4414 self.advance(); if !matches!(self.peek(), Token::LParen) {
4416 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
4417 }
4418 self.advance();
4419 if matches!(self.peek(), Token::Select) {
4421 let inner = self.parse_select_stmt()?;
4422 if !matches!(self.peek(), Token::RParen) {
4423 return Err(self.err(format!(
4424 "expected ')' after IN-subquery, got {:?}",
4425 self.peek()
4426 )));
4427 }
4428 self.advance();
4429 let Statement::Select(s) = inner else {
4430 unreachable!("parse_select_stmt always returns Statement::Select")
4431 };
4432 return Ok(Expr::InSubquery {
4433 expr: Box::new(expr),
4434 subquery: Box::new(s),
4435 negated,
4436 });
4437 }
4438 let mut elements = Vec::new();
4439 if !matches!(self.peek(), Token::RParen) {
4440 loop {
4441 elements.push(self.parse_expr(0)?);
4442 match self.peek() {
4443 Token::Comma => {
4444 self.advance();
4445 }
4446 Token::RParen => break,
4447 other => {
4448 return Err(
4449 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
4450 );
4451 }
4452 }
4453 }
4454 }
4455 self.advance(); let target = Box::new(expr);
4457 let combined = if elements.is_empty() {
4458 Expr::Literal(Literal::Bool(false))
4459 } else {
4460 let mut iter = elements.into_iter();
4461 let first = iter.next().unwrap();
4462 let mut acc = Expr::Binary {
4463 lhs: target.clone(),
4464 op: BinOp::Eq,
4465 rhs: Box::new(first),
4466 };
4467 for elt in iter {
4468 acc = Expr::Binary {
4469 lhs: Box::new(acc),
4470 op: BinOp::Or,
4471 rhs: Box::new(Expr::Binary {
4472 lhs: target.clone(),
4473 op: BinOp::Eq,
4474 rhs: Box::new(elt),
4475 }),
4476 };
4477 }
4478 acc
4479 };
4480 Ok(maybe_not(combined, negated))
4481 }
4482
4483 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
4491 if !matches!(self.peek(), Token::LParen) {
4492 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
4493 }
4494 self.advance();
4495 let field_name = self.expect_ident_like()?;
4496 let field = match field_name.to_ascii_lowercase().as_str() {
4497 "year" => ExtractField::Year,
4498 "month" => ExtractField::Month,
4499 "day" => ExtractField::Day,
4500 "hour" => ExtractField::Hour,
4501 "minute" => ExtractField::Minute,
4502 "second" => ExtractField::Second,
4503 "microsecond" | "microseconds" => ExtractField::Microsecond,
4504 other => {
4505 return Err(self.err(format!(
4506 "unknown EXTRACT field {other:?}; \
4507 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND"
4508 )));
4509 }
4510 };
4511 if !matches!(self.peek(), Token::From) {
4512 return Err(self.err(format!(
4513 "expected FROM after EXTRACT field, got {:?}",
4514 self.peek()
4515 )));
4516 }
4517 self.advance();
4518 let source = self.parse_expr(0)?;
4519 if !matches!(self.peek(), Token::RParen) {
4520 return Err(self.err(format!(
4521 "expected ')' to close EXTRACT, got {:?}",
4522 self.peek()
4523 )));
4524 }
4525 self.advance();
4526 Ok(Expr::Extract {
4527 field,
4528 source: Box::new(source),
4529 })
4530 }
4531
4532 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
4537 let tok = self.advance();
4538 let Token::String(text) = tok else {
4539 return Err(self.err(format!(
4540 "expected string literal after INTERVAL, got {tok:?}"
4541 )));
4542 };
4543 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
4544 message: format!(
4545 "cannot parse INTERVAL {text:?}; \
4546 expected `<n> <unit> [<n> <unit> ...]` with units \
4547 microsecond[s], millisecond[s], second[s], minute[s], \
4548 hour[s], day[s], week[s], month[s], year[s]"
4549 ),
4550 token_pos: self.pos.saturating_sub(1),
4551 })?;
4552 Ok(Expr::Literal(Literal::Interval {
4553 months,
4554 micros,
4555 text,
4556 }))
4557 }
4558
4559 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
4560 let mut elems = Vec::new();
4561 if matches!(self.peek(), Token::RBracket) {
4562 self.advance();
4563 return Ok(Expr::Literal(Literal::Vector(elems)));
4564 }
4565 loop {
4566 let e = self.parse_expr(0)?;
4567 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
4568 message: format!("vector element must be a numeric literal, got {e:?}"),
4569 token_pos: self.pos,
4570 })?;
4571 elems.push(x);
4572 match self.peek() {
4573 Token::Comma => {
4574 self.advance();
4575 }
4576 Token::RBracket => {
4577 self.advance();
4578 break;
4579 }
4580 other => {
4581 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
4582 }
4583 }
4584 }
4585 Ok(Expr::Literal(Literal::Vector(elems)))
4586 }
4587
4588 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
4597 let Token::Ident(s) = self.peek().clone() else {
4598 return NullTreatment::Respect;
4599 };
4600 let is_ignore = s.eq_ignore_ascii_case("ignore");
4601 let is_respect = s.eq_ignore_ascii_case("respect");
4602 if !is_ignore && !is_respect {
4603 return NullTreatment::Respect;
4604 }
4605 if self.pos + 1 < self.tokens.len()
4608 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
4609 && s2.eq_ignore_ascii_case("nulls")
4610 {
4611 self.advance();
4612 self.advance();
4613 return if is_ignore {
4614 NullTreatment::Ignore
4615 } else {
4616 NullTreatment::Respect
4617 };
4618 }
4619 NullTreatment::Respect
4620 }
4621
4622 #[allow(clippy::type_complexity)] fn parse_over_clause(
4625 &mut self,
4626 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
4627 if !matches!(self.peek(), Token::LParen) {
4628 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
4629 }
4630 self.advance();
4631 let mut partition_by = Vec::new();
4632 let mut order_by = Vec::new();
4633 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4635 && s.eq_ignore_ascii_case("partition")
4636 {
4637 self.advance();
4638 if !matches!(self.peek(), Token::By) {
4639 return Err(self.err(format!(
4640 "expected BY after PARTITION, got {:?}",
4641 self.peek()
4642 )));
4643 }
4644 self.advance();
4645 loop {
4646 partition_by.push(self.parse_expr(0)?);
4647 if matches!(self.peek(), Token::Comma) {
4648 self.advance();
4649 continue;
4650 }
4651 break;
4652 }
4653 }
4654 if matches!(self.peek(), Token::Order) {
4656 self.advance();
4657 if !matches!(self.peek(), Token::By) {
4658 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4659 }
4660 self.advance();
4661 loop {
4662 let e = self.parse_expr(0)?;
4663 let desc = if matches!(self.peek(), Token::Desc) {
4664 self.advance();
4665 true
4666 } else if matches!(self.peek(), Token::Asc) {
4667 self.advance();
4668 false
4669 } else {
4670 false
4671 };
4672 order_by.push((e, desc));
4673 if matches!(self.peek(), Token::Comma) {
4674 self.advance();
4675 continue;
4676 }
4677 break;
4678 }
4679 }
4680 let mut frame: Option<WindowFrame> = None;
4684 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
4685 let kind = if s.eq_ignore_ascii_case("rows") {
4686 Some(FrameKind::Rows)
4687 } else if s.eq_ignore_ascii_case("range") {
4688 Some(FrameKind::Range)
4689 } else {
4690 None
4691 };
4692 if let Some(kind) = kind {
4693 self.advance();
4694 frame = Some(self.parse_frame_tail(kind)?);
4695 }
4696 }
4697 if !matches!(self.peek(), Token::RParen) {
4698 return Err(self.err(format!(
4699 "expected ')' to close OVER clause, got {:?}",
4700 self.peek()
4701 )));
4702 }
4703 self.advance();
4704 Ok((partition_by, order_by, frame))
4705 }
4706
4707 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
4713 if matches!(self.peek(), Token::Between) {
4714 self.advance();
4715 let start = self.parse_frame_bound()?;
4716 if !matches!(self.peek(), Token::And) {
4717 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
4718 }
4719 self.advance();
4720 let end = self.parse_frame_bound()?;
4721 Ok(WindowFrame {
4722 kind,
4723 start,
4724 end: Some(end),
4725 })
4726 } else {
4727 let start = self.parse_frame_bound()?;
4728 Ok(WindowFrame {
4729 kind,
4730 start,
4731 end: None,
4732 })
4733 }
4734 }
4735
4736 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
4739 if let Token::Integer(n) = *self.peek() {
4741 self.advance();
4742 let n: u64 = u64::try_from(n).map_err(|_| {
4743 self.err(format!(
4744 "invalid frame offset {n} — expected non-negative integer"
4745 ))
4746 })?;
4747 let dir = self.expect_ident_like()?;
4748 return if dir.eq_ignore_ascii_case("preceding") {
4749 Ok(FrameBound::OffsetPreceding(n))
4750 } else if dir.eq_ignore_ascii_case("following") {
4751 Ok(FrameBound::OffsetFollowing(n))
4752 } else {
4753 Err(self.err(format!(
4754 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
4755 )))
4756 };
4757 }
4758 let first = self.expect_ident_like()?;
4759 if first.eq_ignore_ascii_case("unbounded") {
4760 let dir = self.expect_ident_like()?;
4761 return if dir.eq_ignore_ascii_case("preceding") {
4762 Ok(FrameBound::UnboundedPreceding)
4763 } else if dir.eq_ignore_ascii_case("following") {
4764 Ok(FrameBound::UnboundedFollowing)
4765 } else {
4766 Err(self.err(format!(
4767 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
4768 )))
4769 };
4770 }
4771 if first.eq_ignore_ascii_case("current") {
4772 let row = self.expect_ident_like()?;
4773 if !row.eq_ignore_ascii_case("row") {
4774 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
4775 }
4776 return Ok(FrameBound::CurrentRow);
4777 }
4778 Err(self.err(format!(
4779 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
4780 )))
4781 }
4782
4783 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
4784 if matches!(self.peek(), Token::Dot) {
4785 self.advance();
4786 let name = self.expect_ident_like()?;
4787 return Ok(Expr::Column(ColumnName {
4788 qualifier: Some(first),
4789 name,
4790 }));
4791 }
4792 if matches!(self.peek(), Token::LParen) {
4793 self.advance();
4794 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
4798 self.advance();
4799 if !matches!(self.peek(), Token::RParen) {
4800 return Err(self.err(format!(
4801 "expected ')' after COUNT(*), got {:?}",
4802 self.peek()
4803 )));
4804 }
4805 self.advance();
4806 let null_treatment = self.parse_null_treatment_modifier();
4808 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4809 && s.eq_ignore_ascii_case("over")
4810 {
4811 self.advance();
4812 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4813 return Ok(Expr::WindowFunction {
4814 name: "count_star".into(),
4815 args: Vec::new(),
4816 partition_by,
4817 order_by,
4818 frame,
4819 null_treatment,
4820 });
4821 }
4822 return Ok(Expr::FunctionCall {
4823 name: "count_star".into(),
4824 args: Vec::new(),
4825 });
4826 }
4827 let mut args = Vec::new();
4829 if !matches!(self.peek(), Token::RParen) {
4830 loop {
4831 args.push(self.parse_expr(0)?);
4832 match self.peek() {
4833 Token::Comma => {
4834 self.advance();
4835 }
4836 Token::RParen => break,
4837 other => {
4838 return Err(self.err(format!(
4839 "expected ',' or ')' in function args, got {other:?}"
4840 )));
4841 }
4842 }
4843 }
4844 }
4845 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
4853 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
4854 && s.eq_ignore_ascii_case("over")
4855 {
4856 self.advance();
4857 let (partition_by, order_by, frame) = self.parse_over_clause()?;
4858 return Ok(Expr::WindowFunction {
4859 name: first,
4860 args,
4861 partition_by,
4862 order_by,
4863 frame,
4864 null_treatment,
4865 });
4866 }
4867 return Ok(Expr::FunctionCall { name: first, args });
4868 }
4869 let lc = first.to_ascii_lowercase();
4875 if matches!(
4876 lc.as_str(),
4877 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
4878 ) {
4879 return Ok(Expr::FunctionCall {
4880 name: lc,
4881 args: Vec::new(),
4882 });
4883 }
4884 Ok(Expr::Column(ColumnName {
4885 qualifier: None,
4886 name: first,
4887 }))
4888 }
4889}
4890
4891fn extract_first_column(expr: &Expr) -> Option<String> {
4899 match expr {
4900 Expr::Column(cn) => Some(cn.name.clone()),
4901 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
4902 Expr::Binary { lhs, rhs, .. } => {
4903 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
4904 }
4905 Expr::Unary { expr: e, .. } => extract_first_column(e),
4906 _ => None,
4907 }
4908}
4909
4910fn maybe_not(expr: Expr, negated: bool) -> Expr {
4911 if negated {
4912 Expr::Unary {
4913 op: UnOp::Not,
4914 expr: Box::new(expr),
4915 }
4916 } else {
4917 expr
4918 }
4919}
4920
4921fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
4922 let pair = match tok {
4923 Token::Or => (BinOp::Or, 1),
4924 Token::And => (BinOp::And, 2),
4925 Token::Eq => (BinOp::Eq, 4),
4926 Token::NotEq => (BinOp::NotEq, 4),
4927 Token::Lt => (BinOp::Lt, 4),
4928 Token::LtEq => (BinOp::LtEq, 4),
4929 Token::Gt => (BinOp::Gt, 4),
4930 Token::GtEq => (BinOp::GtEq, 4),
4931 Token::L2Distance => (BinOp::L2Distance, 5),
4934 Token::InnerProduct => (BinOp::InnerProduct, 5),
4935 Token::CosineDistance => (BinOp::CosineDistance, 5),
4936 Token::Plus => (BinOp::Add, 6),
4937 Token::Minus => (BinOp::Sub, 6),
4938 Token::Concat => (BinOp::Concat, 6),
4941 Token::Star => (BinOp::Mul, 7),
4942 Token::Slash => (BinOp::Div, 7),
4943 Token::JsonGet => (BinOp::JsonGet, 7),
4947 Token::JsonGetText => (BinOp::JsonGetText, 7),
4948 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
4949 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
4950 Token::JsonContains => (BinOp::JsonContains, 7),
4951 Token::TsMatch => (BinOp::TsMatch, 4),
4955 _ => return None,
4956 };
4957 Some(pair)
4958}
4959
4960#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
4961fn extract_numeric_literal(e: &Expr) -> Option<f32> {
4966 match e {
4967 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
4968 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
4969 Expr::Unary {
4970 op: UnOp::Neg,
4971 expr,
4972 } => extract_numeric_literal(expr).map(|x| -x),
4973 _ => None,
4974 }
4975}
4976
4977pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
4985 let parts: Vec<&str> = s.split_whitespace().collect();
4986 if parts.is_empty() || !parts.len().is_multiple_of(2) {
4987 return None;
4988 }
4989 let mut months: i32 = 0;
4990 let mut micros: i64 = 0;
4991 let mut i = 0;
4992 while i < parts.len() {
4993 let n: i64 = parts[i].parse().ok()?;
4994 let unit = parts[i + 1].to_ascii_lowercase();
4995 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
4996 match unit_stripped {
4997 "microsecond" => micros = micros.checked_add(n)?,
4998 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
4999 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
5000 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
5001 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
5002 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
5003 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
5004 "month" => {
5005 let n32 = i32::try_from(n).ok()?;
5006 months = months.checked_add(n32)?;
5007 }
5008 "year" => {
5009 let n32 = i32::try_from(n).ok()?;
5010 months = months.checked_add(n32.checked_mul(12)?)?;
5011 }
5012 _ => return None,
5013 }
5014 i += 2;
5015 }
5016 Some((months, micros))
5017}
5018
5019fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
5030 Some(match ident.to_ascii_lowercase().as_str() {
5031 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
5032 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
5033 "bigint" => ColumnTypeName::BigInt,
5034 "float" | "double" | "real" => ColumnTypeName::Float,
5035 "text" => ColumnTypeName::Text,
5036 "bool" | "boolean" => ColumnTypeName::Bool,
5037 "date" => ColumnTypeName::Date,
5038 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
5039 "timestamptz" => ColumnTypeName::Timestamptz,
5040 "json" => ColumnTypeName::Json,
5041 "jsonb" => ColumnTypeName::Jsonb,
5042 "bytea" | "bytes" => ColumnTypeName::Bytes,
5043 "tsvector" => ColumnTypeName::TsVector,
5044 "tsquery" => ColumnTypeName::TsQuery,
5045 _ => return None,
5046 })
5047}
5048
5049pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5076 parse_plpgsql_body(body)
5077}
5078
5079fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
5080 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
5084 message: alloc::format!("plpgsql body lex error: {e}"),
5085 token_pos: 0,
5086 })?;
5087 let mut parser = Parser::new(tokens);
5088 parser.parse_plpgsql_block()
5089}
5090
5091#[cfg(test)]
5092mod tests {
5093 use super::*;
5094 use alloc::string::ToString;
5095
5096 fn parse(s: &str) -> Statement {
5097 parse_statement(s).expect("parse ok")
5098 }
5099
5100 fn lit_int(n: i64) -> Expr {
5101 Expr::Literal(Literal::Integer(n))
5102 }
5103
5104 fn col(name: &str) -> Expr {
5105 Expr::Column(ColumnName {
5106 qualifier: None,
5107 name: name.into(),
5108 })
5109 }
5110
5111 #[test]
5112 fn select_single_integer() {
5113 let s = parse("SELECT 1");
5114 let Statement::Select(s) = s else {
5115 panic!("expected SELECT")
5116 };
5117 assert_eq!(s.items.len(), 1);
5118 assert!(s.from.is_none());
5119 assert!(s.where_.is_none());
5120 }
5121
5122 #[test]
5123 fn select_multiple_literal_kinds() {
5124 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
5125 let Statement::Select(s) = s else {
5126 panic!("expected SELECT")
5127 };
5128 assert_eq!(s.items.len(), 5);
5129 }
5130
5131 #[test]
5132 fn select_wildcard_from_table() {
5133 let s = parse("SELECT * FROM users");
5134 let Statement::Select(s) = s else {
5135 panic!("expected SELECT")
5136 };
5137 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
5138 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
5139 }
5140
5141 #[test]
5142 fn select_with_table_alias() {
5143 let s = parse("SELECT * FROM users AS u");
5144 let Statement::Select(s) = s else {
5145 panic!("expected SELECT")
5146 };
5147 let t = &s.from.as_ref().unwrap().primary;
5148 assert_eq!(t.name, "users");
5149 assert_eq!(t.alias.as_deref(), Some("u"));
5150 }
5151
5152 #[test]
5153 fn select_with_where_eq() {
5154 let s = parse("SELECT a FROM t WHERE a = 1");
5155 let Statement::Select(s) = s else {
5156 panic!("expected SELECT")
5157 };
5158 let w = s.where_.unwrap();
5159 assert_eq!(
5160 w,
5161 Expr::Binary {
5162 lhs: Box::new(col("a")),
5163 op: BinOp::Eq,
5164 rhs: Box::new(lit_int(1)),
5165 }
5166 );
5167 }
5168
5169 #[test]
5170 fn arithmetic_precedence() {
5171 let s = parse("SELECT 1 + 2 * 3");
5172 let Statement::Select(s) = s else {
5173 panic!("expected SELECT")
5174 };
5175 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5176 panic!("wildcard?")
5177 };
5178 assert_eq!(
5179 expr,
5180 &Expr::Binary {
5181 lhs: Box::new(lit_int(1)),
5182 op: BinOp::Add,
5183 rhs: Box::new(Expr::Binary {
5184 lhs: Box::new(lit_int(2)),
5185 op: BinOp::Mul,
5186 rhs: Box::new(lit_int(3)),
5187 }),
5188 }
5189 );
5190 }
5191
5192 #[test]
5193 fn parentheses_override_precedence() {
5194 let s = parse("SELECT (1 + 2) * 3");
5195 let Statement::Select(s) = s else {
5196 panic!("expected SELECT")
5197 };
5198 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5199 panic!()
5200 };
5201 assert_eq!(
5202 expr,
5203 &Expr::Binary {
5204 lhs: Box::new(Expr::Binary {
5205 lhs: Box::new(lit_int(1)),
5206 op: BinOp::Add,
5207 rhs: Box::new(lit_int(2)),
5208 }),
5209 op: BinOp::Mul,
5210 rhs: Box::new(lit_int(3)),
5211 }
5212 );
5213 }
5214
5215 #[test]
5216 fn not_binds_below_comparison() {
5217 let s = parse("SELECT NOT a = 1 FROM t");
5219 let Statement::Select(s) = s else {
5220 panic!("expected SELECT")
5221 };
5222 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5223 panic!()
5224 };
5225 assert_eq!(
5226 expr,
5227 &Expr::Unary {
5228 op: UnOp::Not,
5229 expr: Box::new(Expr::Binary {
5230 lhs: Box::new(col("a")),
5231 op: BinOp::Eq,
5232 rhs: Box::new(lit_int(1)),
5233 }),
5234 }
5235 );
5236 }
5237
5238 #[test]
5239 fn unary_minus_binds_above_multiplication() {
5240 let s = parse("SELECT -a * 2 FROM t");
5242 let Statement::Select(s) = s else {
5243 panic!("expected SELECT")
5244 };
5245 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5246 panic!()
5247 };
5248 assert_eq!(
5249 expr,
5250 &Expr::Binary {
5251 lhs: Box::new(Expr::Unary {
5252 op: UnOp::Neg,
5253 expr: Box::new(col("a")),
5254 }),
5255 op: BinOp::Mul,
5256 rhs: Box::new(lit_int(2)),
5257 }
5258 );
5259 }
5260
5261 #[test]
5262 fn qualified_column() {
5263 let s = parse("SELECT t.col FROM t");
5264 let Statement::Select(s) = s else {
5265 panic!("expected SELECT")
5266 };
5267 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5268 panic!()
5269 };
5270 assert_eq!(
5271 expr,
5272 &Expr::Column(ColumnName {
5273 qualifier: Some("t".into()),
5274 name: "col".into()
5275 })
5276 );
5277 }
5278
5279 #[test]
5280 fn select_item_alias_with_as() {
5281 let s = parse("SELECT a AS y FROM t");
5282 let Statement::Select(s) = s else {
5283 panic!("expected SELECT")
5284 };
5285 let SelectItem::Expr { alias, .. } = &s.items[0] else {
5286 panic!()
5287 };
5288 assert_eq!(alias.as_deref(), Some("y"));
5289 }
5290
5291 #[test]
5292 fn trailing_semicolon_accepted() {
5293 let s = parse("SELECT 1;");
5294 let Statement::Select(s) = s else {
5295 panic!("expected SELECT")
5296 };
5297 assert_eq!(s.items.len(), 1);
5298 }
5299
5300 #[test]
5301 fn boolean_chain_with_and_or_not() {
5302 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
5304 let Statement::Select(s) = s else {
5305 panic!("expected SELECT")
5306 };
5307 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5308 panic!()
5309 };
5310 let expected = Expr::Binary {
5311 lhs: Box::new(Expr::Unary {
5312 op: UnOp::Not,
5313 expr: Box::new(col("a")),
5314 }),
5315 op: BinOp::Or,
5316 rhs: Box::new(Expr::Binary {
5317 lhs: Box::new(col("b")),
5318 op: BinOp::And,
5319 rhs: Box::new(Expr::Unary {
5320 op: UnOp::Not,
5321 expr: Box::new(col("c")),
5322 }),
5323 }),
5324 };
5325 assert_eq!(expr, &expected);
5326 }
5327
5328 #[test]
5329 fn empty_input_errors() {
5330 let err = parse_statement("").unwrap_err();
5331 assert!(err.message.contains("SELECT"));
5332 }
5333
5334 #[test]
5335 fn unmatched_paren_errors() {
5336 assert!(parse_statement("SELECT (1 + 2").is_err());
5337 }
5338
5339 #[test]
5340 fn display_round_trip_simple_select() {
5341 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
5342 let text = original.to_string();
5343 let again = parse_statement(&text).expect("re-parse");
5344 assert_eq!(original, again);
5345 }
5346
5347 #[test]
5350 fn create_table_single_column() {
5351 let s = parse("CREATE TABLE foo (a INT)");
5352 let Statement::CreateTable(c) = s else {
5353 panic!("expected CreateTable")
5354 };
5355 assert_eq!(c.name, "foo");
5356 assert_eq!(c.columns.len(), 1);
5357 assert_eq!(c.columns[0].name, "a");
5358 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5359 assert!(c.columns[0].nullable);
5360 }
5361
5362 #[test]
5363 fn create_table_multi_column_with_not_null_mix() {
5364 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
5365 let Statement::CreateTable(c) = s else {
5366 panic!()
5367 };
5368 assert_eq!(c.columns.len(), 4);
5369 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
5370 assert!(!c.columns[0].nullable);
5371 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
5372 assert!(c.columns[1].nullable);
5373 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
5374 assert!(!c.columns[2].nullable);
5375 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
5376 }
5377
5378 #[test]
5379 fn create_table_bigint_supported() {
5380 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
5381 let Statement::CreateTable(c) = s else {
5382 panic!()
5383 };
5384 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
5385 }
5386
5387 #[test]
5388 fn create_table_vector_default_is_f32() {
5389 let s = parse("CREATE TABLE t (v VECTOR(128))");
5390 let Statement::CreateTable(c) = s else {
5391 panic!()
5392 };
5393 assert_eq!(
5394 c.columns[0].ty,
5395 ColumnTypeName::Vector {
5396 dim: 128,
5397 encoding: VecEncoding::F32,
5398 },
5399 );
5400 }
5401
5402 #[test]
5403 fn create_table_vector_using_sq8() {
5404 for sql in [
5407 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
5408 "CREATE TABLE t (v VECTOR(128) using sq8)",
5409 ] {
5410 let s = parse(sql);
5411 let Statement::CreateTable(c) = s else {
5412 panic!()
5413 };
5414 assert_eq!(
5415 c.columns[0].ty,
5416 ColumnTypeName::Vector {
5417 dim: 128,
5418 encoding: VecEncoding::Sq8,
5419 },
5420 "{sql}",
5421 );
5422 }
5423 }
5424
5425 #[test]
5426 fn create_table_vector_using_unknown_errors() {
5427 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
5428 assert!(
5429 err.message.contains("unknown vector encoding"),
5430 "got: {}",
5431 err.message
5432 );
5433 }
5434
5435 #[test]
5436 fn vector_using_sq8_display_roundtrips() {
5437 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
5440 let Statement::CreateTable(c) = s else {
5441 panic!()
5442 };
5443 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
5444 }
5445
5446 #[test]
5447 fn parser_recognises_placeholders() {
5448 use crate::ast::{Expr, SelectItem, Statement};
5449 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
5451 let Statement::Select(sel) = s else { panic!() };
5452 assert!(matches!(
5453 sel.items[0],
5454 SelectItem::Expr {
5455 expr: Expr::Placeholder(1),
5456 alias: None
5457 }
5458 ));
5459 let SelectItem::Expr {
5461 expr: Expr::Binary { lhs, rhs, .. },
5462 ..
5463 } = &sel.items[1]
5464 else {
5465 panic!()
5466 };
5467 assert!(matches!(**lhs, Expr::Placeholder(2)));
5468 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
5469 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
5471 panic!()
5472 };
5473 assert!(matches!(**rhs, Expr::Placeholder(3)));
5474 }
5475
5476 #[test]
5477 fn parser_rejects_dollar_zero() {
5478 assert!(parse_statement("SELECT $0").is_err());
5480 }
5481
5482 #[test]
5483 fn placeholder_display_roundtrips() {
5484 let s = parse("SELECT $42 FROM t");
5487 let printed = s.to_string();
5488 assert!(printed.contains("$42"));
5489 let again = parse(&printed);
5490 assert_eq!(s, again);
5491 }
5492
5493 #[test]
5494 fn alter_index_rebuild_bare() {
5495 use crate::ast::{AlterIndexTarget, Statement};
5496 let s = parse("ALTER INDEX my_idx REBUILD");
5497 let Statement::AlterIndex(a) = s else {
5498 panic!("expected AlterIndex, got {s:?}")
5499 };
5500 assert_eq!(a.name, "my_idx");
5501 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
5502 }
5503
5504 #[test]
5505 fn alter_index_rebuild_with_encoding() {
5506 use crate::ast::{AlterIndexTarget, Statement};
5507 for (sql, want) in [
5508 (
5509 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
5510 VecEncoding::F32,
5511 ),
5512 (
5513 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
5514 VecEncoding::Sq8,
5515 ),
5516 (
5517 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5518 VecEncoding::F16,
5519 ),
5520 ] {
5521 let s = parse(sql);
5522 let Statement::AlterIndex(a) = s else {
5523 panic!("{sql}: expected AlterIndex")
5524 };
5525 assert_eq!(a.name, "my_idx");
5526 assert_eq!(
5527 a.target,
5528 AlterIndexTarget::Rebuild {
5529 encoding: Some(want)
5530 },
5531 "{sql}"
5532 );
5533 }
5534 }
5535
5536 #[test]
5537 fn alter_index_rebuild_unknown_encoding_errors() {
5538 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
5539 assert!(
5540 err.message.contains("unknown vector encoding"),
5541 "got: {}",
5542 err.message
5543 );
5544 }
5545
5546 #[test]
5547 fn alter_index_rebuild_display_roundtrips() {
5548 for (input, want) in [
5549 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
5550 (
5551 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5552 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
5553 ),
5554 (
5555 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5556 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
5557 ),
5558 ] {
5559 let s = parse(input);
5560 assert_eq!(s.to_string(), want);
5561 }
5562 }
5563
5564 #[test]
5565 fn create_table_unknown_type_errors() {
5566 let err = parse_statement("CREATE TABLE x (a xml)").unwrap_err();
5569 assert!(err.message.contains("unsupported column type"));
5570 }
5571
5572 #[test]
5573 fn create_table_missing_table_keyword_errors() {
5574 assert!(parse_statement("CREATE x (a INT)").is_err());
5575 }
5576
5577 #[test]
5578 fn insert_single_value() {
5579 let s = parse("INSERT INTO foo VALUES (42)");
5580 let Statement::Insert(i) = s else {
5581 panic!("expected Insert")
5582 };
5583 assert_eq!(i.table, "foo");
5584 assert_eq!(i.rows.len(), 1);
5585 assert_eq!(i.rows[0].len(), 1);
5586 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
5587 }
5588
5589 #[test]
5590 fn insert_multi_value_with_mixed_literals() {
5591 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
5592 let Statement::Insert(i) = s else { panic!() };
5593 assert_eq!(i.rows.len(), 1);
5594 assert_eq!(i.rows[0].len(), 5);
5595 }
5596
5597 #[test]
5598 fn insert_missing_into_errors() {
5599 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
5600 }
5601
5602 #[test]
5603 fn create_table_round_trip() {
5604 let original =
5605 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
5606 let text = original.to_string();
5607 let again = parse_statement(&text).expect("re-parse");
5608 assert_eq!(original, again);
5609 }
5610
5611 #[test]
5612 fn insert_round_trip_with_negation_and_string() {
5613 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
5614 let text = original.to_string();
5615 let again = parse_statement(&text).expect("re-parse");
5616 assert_eq!(original, again);
5617 }
5618
5619 #[test]
5620 fn unknown_keyword_at_statement_start_errors() {
5621 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
5624 assert!(err.message.contains("expected SELECT"));
5625 }
5626
5627 #[test]
5630 fn create_index_basic() {
5631 let s = parse("CREATE INDEX idx_id ON users (id)");
5632 let Statement::CreateIndex(c) = s else {
5633 panic!("expected CreateIndex")
5634 };
5635 assert_eq!(c.name, "idx_id");
5636 assert_eq!(c.table, "users");
5637 assert_eq!(c.column, "id");
5638 }
5639
5640 #[test]
5641 fn create_index_missing_on_errors() {
5642 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
5643 }
5644
5645 #[test]
5646 fn create_index_missing_paren_errors() {
5647 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
5648 }
5649
5650 #[test]
5651 fn create_index_round_trip() {
5652 let original = parse("CREATE INDEX by_name ON users (name)");
5653 let again = parse_statement(&original.to_string()).unwrap();
5654 assert_eq!(original, again);
5655 }
5656
5657 #[test]
5660 fn create_unique_index_basic() {
5661 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
5662 let Statement::CreateIndex(c) = s else {
5663 panic!("expected CreateIndex");
5664 };
5665 assert!(c.is_unique);
5666 assert_eq!(c.column, "a");
5667 assert!(c.partial_predicate.is_none());
5668 }
5669
5670 #[test]
5671 fn create_unique_index_partial() {
5672 let s = parse(
5674 "CREATE UNIQUE INDEX idx_email_templates_user_default \
5675 ON email_templates (user_address) WHERE is_default = true",
5676 );
5677 let Statement::CreateIndex(c) = s else {
5678 panic!("expected CreateIndex");
5679 };
5680 assert!(c.is_unique);
5681 assert_eq!(c.table, "email_templates");
5682 assert_eq!(c.column, "user_address");
5683 assert!(c.partial_predicate.is_some());
5684 }
5685
5686 #[test]
5687 fn create_unique_index_composite_with_predicate() {
5688 let s = parse(
5690 "CREATE UNIQUE INDEX uq_calendar_events_instance \
5691 ON calendar_events (calendar_id, uid, recurrence_id) \
5692 WHERE recurrence_id IS NOT NULL",
5693 );
5694 let Statement::CreateIndex(c) = s else {
5695 panic!("expected CreateIndex");
5696 };
5697 assert!(c.is_unique);
5698 assert_eq!(c.column, "calendar_id");
5699 assert_eq!(
5700 c.extra_columns,
5701 vec!["uid".to_string(), "recurrence_id".to_string()]
5702 );
5703 assert!(c.partial_predicate.is_some());
5704 }
5705
5706 #[test]
5707 fn create_unique_index_using_btree_ok() {
5708 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
5709 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
5710 }
5711
5712 #[test]
5713 fn create_unique_index_using_hnsw_rejected() {
5714 let err =
5715 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
5716 assert!(err.message.contains("UNIQUE"), "{}", err.message);
5717 }
5718
5719 #[test]
5720 fn create_unique_index_round_trip() {
5721 let original = parse(
5722 "CREATE UNIQUE INDEX uq_calendar_events_master \
5723 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
5724 );
5725 let again = parse_statement(&original.to_string()).unwrap();
5726 assert_eq!(original, again);
5727 }
5728
5729 #[test]
5730 fn create_unique_without_index_errors() {
5731 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
5732 assert!(err.message.contains("INDEX"), "{}", err.message);
5733 }
5734
5735 #[test]
5738 fn create_table_bytea_column() {
5739 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
5740 let Statement::CreateTable(c) = s else {
5741 panic!("expected CreateTable");
5742 };
5743 assert_eq!(c.columns.len(), 2);
5744 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
5745 assert!(!c.columns[1].nullable);
5746 }
5747
5748 #[test]
5749 fn create_table_bytes_alias_column() {
5750 let s = parse("CREATE TABLE t (blob BYTES)");
5751 let Statement::CreateTable(c) = s else {
5752 panic!("expected CreateTable");
5753 };
5754 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
5755 }
5756
5757 #[test]
5758 fn bytea_round_trip_display() {
5759 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
5760 let again = parse_statement(&original.to_string()).unwrap();
5761 assert_eq!(original, again);
5762 }
5763
5764 #[test]
5767 fn begin_commit_rollback_parse_as_unit_variants() {
5768 assert_eq!(parse("BEGIN"), Statement::Begin);
5769 assert_eq!(parse("COMMIT"), Statement::Commit);
5770 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
5771 assert_eq!(parse("BEGIN;"), Statement::Begin);
5773 }
5774
5775 #[test]
5778 fn inner_product_binop_parses() {
5779 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
5780 let Statement::Select(s) = s else { panic!() };
5781 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5782 panic!()
5783 };
5784 assert!(matches!(
5785 expr,
5786 Expr::Binary {
5787 op: BinOp::InnerProduct,
5788 ..
5789 }
5790 ));
5791 }
5792
5793 #[test]
5794 fn cosine_distance_binop_parses() {
5795 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
5796 let Statement::Select(s) = s else { panic!() };
5797 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5798 panic!()
5799 };
5800 assert!(matches!(
5801 expr,
5802 Expr::Binary {
5803 op: BinOp::CosineDistance,
5804 ..
5805 }
5806 ));
5807 }
5808
5809 #[test]
5810 fn vector_cast_postfix_wraps_string_literal() {
5811 let s = parse("SELECT '[1,2,3]'::vector FROM t");
5812 let Statement::Select(s) = s else { panic!() };
5813 let SelectItem::Expr { expr, .. } = &s.items[0] else {
5814 panic!()
5815 };
5816 assert!(matches!(
5817 expr,
5818 Expr::Cast {
5819 target: CastTarget::Vector,
5820 ..
5821 }
5822 ));
5823 }
5824
5825 #[test]
5826 fn unsupported_cast_target_errors() {
5827 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
5829 assert!(err.message.contains("unsupported cast target"));
5830 }
5831
5832 #[test]
5833 fn tx_statements_round_trip() {
5834 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
5835 let original = parse(q);
5836 let again = parse_statement(&original.to_string()).unwrap();
5837 assert_eq!(original, again);
5838 }
5839 }
5840
5841 #[test]
5842 fn interval_text_parsing_units() {
5843 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
5845 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
5846 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
5847 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
5848 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
5850 assert_eq!(
5851 parse_interval_text("1 day 2 hours"),
5852 Some((0, 86_400_000_000 + 7_200_000_000))
5853 );
5854 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
5856 assert_eq!(parse_interval_text(""), None);
5858 assert_eq!(parse_interval_text("garbage"), None);
5859 assert_eq!(parse_interval_text("1 fortnight"), None);
5860 assert_eq!(parse_interval_text("1"), None);
5861 }
5862
5863 #[test]
5864 fn interval_literal_roundtrips_via_display() {
5865 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
5866 let s = parsed.to_string();
5867 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
5869 let again = parse_statement(&s).unwrap();
5871 assert_eq!(parsed, again);
5872 }
5873
5874 #[test]
5877 fn parser_recognises_create_publication_bare() {
5878 let s = parse("CREATE PUBLICATION pub_a");
5879 let Statement::CreatePublication(p) = s else {
5880 panic!("expected CreatePublication, got {s:?}")
5881 };
5882 assert_eq!(p.name, "pub_a");
5883 assert_eq!(p.scope, PublicationScope::AllTables);
5884 }
5885
5886 #[test]
5887 fn parser_recognises_create_publication_for_all_tables() {
5888 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
5889 let Statement::CreatePublication(p) = s else {
5890 panic!("expected CreatePublication, got {s:?}")
5891 };
5892 assert_eq!(p.name, "pub_a");
5893 assert_eq!(p.scope, PublicationScope::AllTables);
5894 }
5895
5896 #[test]
5897 fn parser_recognises_drop_publication() {
5898 let s = parse("DROP PUBLICATION pub_a");
5899 let Statement::DropPublication(name) = s else {
5900 panic!("expected DropPublication, got {s:?}")
5901 };
5902 assert_eq!(name, "pub_a");
5903 }
5904
5905 #[test]
5906 fn parser_recognises_for_table_list() {
5907 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
5908 let Statement::CreatePublication(p) = s else {
5909 panic!("expected CreatePublication, got {s:?}")
5910 };
5911 assert_eq!(p.name, "pub_a");
5912 let PublicationScope::ForTables(ts) = p.scope else {
5913 panic!("expected ForTables scope")
5914 };
5915 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
5916 }
5917
5918 #[test]
5919 fn parser_recognises_for_tables_plural() {
5920 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
5922 let Statement::CreatePublication(p) = s else {
5923 panic!("expected CreatePublication, got {s:?}")
5924 };
5925 let PublicationScope::ForTables(ts) = p.scope else {
5926 panic!("expected ForTables")
5927 };
5928 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5929 }
5930
5931 #[test]
5932 fn parser_recognises_for_all_tables_except_list() {
5933 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
5934 let Statement::CreatePublication(p) = s else {
5935 panic!()
5936 };
5937 let PublicationScope::AllTablesExcept(ts) = p.scope else {
5938 panic!("expected AllTablesExcept")
5939 };
5940 assert_eq!(ts, alloc::vec!["t1", "t2"]);
5941 }
5942
5943 #[test]
5944 fn parser_rejects_for_table_with_empty_list() {
5945 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
5947 .expect_err("must error on empty list");
5948 assert!(!err.message.is_empty());
5951 }
5952
5953 #[test]
5954 fn parser_recognises_show_publications() {
5955 let s = parse("SHOW PUBLICATIONS");
5958 assert!(matches!(s, Statement::ShowPublications));
5959 }
5960
5961 #[test]
5964 fn parser_recognises_create_subscription_single_publication() {
5965 let s = parse(
5966 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
5967 );
5968 let Statement::CreateSubscription(c) = s else {
5969 panic!("expected CreateSubscription, got {s:?}")
5970 };
5971 assert_eq!(c.name, "sub_a");
5972 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
5973 assert_eq!(c.publications, alloc::vec!["pub_a"]);
5974 }
5975
5976 #[test]
5977 fn parser_recognises_create_subscription_multi_publication() {
5978 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
5979 let Statement::CreateSubscription(c) = s else {
5980 panic!()
5981 };
5982 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
5983 }
5984
5985 #[test]
5986 fn parser_rejects_create_subscription_missing_connection() {
5987 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
5988 .expect_err("must error on missing CONNECTION");
5989 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
5990 }
5991
5992 #[test]
5993 fn parser_rejects_create_subscription_missing_publication() {
5994 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
5995 .expect_err("must error on missing PUBLICATION");
5996 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
5997 }
5998
5999 #[test]
6000 fn parser_recognises_drop_subscription() {
6001 let s = parse("DROP SUBSCRIPTION sub_a");
6002 let Statement::DropSubscription(name) = s else {
6003 panic!("expected DropSubscription, got {s:?}")
6004 };
6005 assert_eq!(name, "sub_a");
6006 }
6007
6008 #[test]
6009 fn parser_recognises_show_subscriptions() {
6010 let s = parse("SHOW SUBSCRIPTIONS");
6011 assert!(matches!(s, Statement::ShowSubscriptions));
6012 }
6013
6014 #[test]
6015 fn parser_recognises_wait_for_wal_position_no_timeout() {
6016 let s = parse("WAIT FOR WAL POSITION 12345");
6017 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6018 panic!("expected WaitForWalPosition, got {s:?}")
6019 };
6020 assert_eq!(pos, 12345);
6021 assert!(timeout_ms.is_none());
6022 }
6023
6024 #[test]
6025 fn parser_recognises_wait_for_wal_position_with_timeout() {
6026 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
6027 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
6028 panic!()
6029 };
6030 assert_eq!(pos, 67890);
6031 assert_eq!(timeout_ms, Some(5000));
6032 }
6033
6034 #[test]
6035 fn parser_rejects_wait_with_negative_position() {
6036 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
6042 assert!(!err.message.is_empty());
6043 }
6044
6045 #[test]
6046 fn parser_recognises_bare_analyze() {
6047 let s = parse("ANALYZE");
6048 assert!(matches!(s, Statement::Analyze(None)));
6049 }
6050
6051 #[test]
6052 fn parser_recognises_analyze_with_table() {
6053 let s = parse("ANALYZE users");
6054 let Statement::Analyze(Some(name)) = s else {
6055 panic!("expected Analyze, got {s:?}")
6056 };
6057 assert_eq!(name, "users");
6058 }
6059
6060 #[test]
6061 fn parser_recognises_analyze_with_quoted_table() {
6062 let s = parse("ANALYZE \"Mixed Case\"");
6063 let Statement::Analyze(Some(name)) = s else {
6064 panic!()
6065 };
6066 assert_eq!(name, "Mixed Case");
6067 }
6068
6069 #[test]
6070 fn parser_rejects_analyze_with_garbage_token() {
6071 let err = parse_statement("ANALYZE 42").expect_err("must error");
6072 assert!(!err.message.is_empty());
6073 }
6074
6075 #[test]
6076 fn analyze_display_roundtrips() {
6077 for sql in ["ANALYZE", "ANALYZE users"] {
6078 let s = parse(sql);
6079 let printed = s.to_string();
6080 let again = parse_statement(&printed)
6081 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6082 assert_eq!(s, again);
6083 }
6084 }
6085
6086 #[test]
6087 fn wait_for_display_roundtrips() {
6088 for sql in [
6089 "WAIT FOR WAL POSITION 12345",
6090 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
6091 ] {
6092 let s = parse(sql);
6093 let printed = s.to_string();
6094 let again = parse_statement(&printed)
6095 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6096 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6097 }
6098 }
6099
6100 #[test]
6101 fn subscription_ddl_display_roundtrips() {
6102 for sql in [
6103 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
6104 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
6105 "DROP SUBSCRIPTION sub_a",
6106 "SHOW SUBSCRIPTIONS",
6107 ] {
6108 let s = parse(sql);
6109 let printed = s.to_string();
6110 let again = parse_statement(&printed)
6111 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6112 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6113 }
6114 }
6115
6116 #[test]
6117 fn parser_drop_dispatches_user_vs_publication() {
6118 let s = parse("DROP USER 'alice'");
6121 let Statement::DropUser(name) = s else {
6122 panic!("expected DropUser, got {s:?}")
6123 };
6124 assert_eq!(name, "alice");
6125 let s = parse("DROP PUBLICATION p1");
6127 assert!(matches!(s, Statement::DropPublication(_)));
6128 }
6129
6130 #[test]
6131 fn publication_ddl_display_roundtrips() {
6132 for sql in [
6135 "CREATE PUBLICATION pub_a",
6136 "CREATE PUBLICATION pub_a FOR ALL TABLES",
6137 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
6138 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
6139 "DROP PUBLICATION pub_a",
6140 "SHOW PUBLICATIONS",
6141 ] {
6142 let s = parse(sql);
6143 let printed = s.to_string();
6144 let again = parse_statement(&printed)
6145 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6146 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6147 }
6148 }
6149
6150 #[test]
6153 fn create_function_returns_trigger_plpgsql_minimal() {
6154 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
6155 let s = parse(sql);
6156 let Statement::CreateFunction(f) = s else {
6157 panic!("expected CreateFunction");
6158 };
6159 assert_eq!(f.name, "noop");
6160 assert!(!f.or_replace);
6161 assert!(f.args.is_empty());
6162 assert!(matches!(f.returns, FunctionReturn::Trigger));
6163 assert_eq!(f.language, "plpgsql");
6164 let FunctionBody::PlPgSql(block) = f.body else {
6165 panic!("expected PlPgSql body");
6166 };
6167 assert_eq!(block.statements.len(), 1);
6168 assert!(matches!(
6169 block.statements[0],
6170 PlPgSqlStmt::Return(ReturnTarget::New)
6171 ));
6172 }
6173
6174 #[test]
6175 fn create_function_or_replace_with_assignment() {
6176 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
6179BEGIN
6180 NEW.search_vector := to_tsvector('english', NEW.subject);
6181 RETURN NEW;
6182END;
6183$$";
6184 let s = parse(sql);
6185 let Statement::CreateFunction(f) = s else {
6186 panic!("expected CreateFunction");
6187 };
6188 assert!(f.or_replace);
6189 let FunctionBody::PlPgSql(block) = &f.body else {
6190 panic!("expected PlPgSql body");
6191 };
6192 assert_eq!(block.statements.len(), 2);
6193 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
6195 panic!("expected Assign as first stmt");
6196 };
6197 match target {
6198 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
6199 other => panic!("expected NEW.col, got {other:?}"),
6200 }
6201 assert!(matches!(
6203 block.statements[1],
6204 PlPgSqlStmt::Return(ReturnTarget::New)
6205 ));
6206 }
6207
6208 #[test]
6209 fn create_trigger_after_insert_or_update() {
6210 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
6211 let s = parse(sql);
6212 let Statement::CreateTrigger(t) = s else {
6213 panic!("expected CreateTrigger");
6214 };
6215 assert_eq!(t.name, "tg");
6216 assert_eq!(t.table, "messages");
6217 assert_eq!(t.timing, TriggerTiming::After);
6218 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
6219 assert_eq!(t.for_each, TriggerForEach::Row);
6220 assert_eq!(t.function, "update_sv");
6221 }
6222
6223 #[test]
6224 fn create_trigger_before_delete_execute_procedure_alias() {
6225 let sql =
6227 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
6228 let s = parse(sql);
6229 let Statement::CreateTrigger(t) = s else {
6230 panic!("expected CreateTrigger");
6231 };
6232 assert_eq!(t.timing, TriggerTiming::Before);
6233 assert_eq!(t.events, vec![TriggerEvent::Delete]);
6234 }
6235
6236 #[test]
6237 fn drop_trigger_if_exists_round_trips() {
6238 let s = Statement::DropTrigger {
6243 name: "tg".into(),
6244 table: "messages".into(),
6245 if_exists: true,
6246 };
6247 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
6248 }
6249
6250 #[test]
6251 fn trigger_ddl_display_roundtrips_through_parser() {
6252 for sql in [
6256 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
6257 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
6258 ] {
6259 let s = parse(sql);
6260 let printed = s.to_string();
6261 let again = parse_statement(&printed)
6262 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
6263 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
6264 }
6265 }
6266}