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, Collation, 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, RangeKindAst, ReturnTarget, SelectItem, SelectStatement,
28 Statement, TableRef, TriggerEvent, TriggerForEach, TriggerTiming, UnOp, UnionKind, VecEncoding,
29 WindowFrame,
30};
31use crate::lexer::{self, LexError, Token};
32
33fn is_dump_noise_statement(lc: &str) -> bool {
39 matches!(
40 lc,
41 "comment"
44 | "grant"
45 | "revoke"
46 | "lock"
48 | "unlock"
49 | "optimize"
53 | "check"
54 | "use"
55 | "\\restrict"
60 | "\\unrestrict"
61 | "delimiter"
70 )
71}
72
73fn is_vector_opclass_name(name: &str) -> bool {
81 let lc = name.to_ascii_lowercase();
82 matches!(
83 lc.as_str(),
84 "vector_cosine_ops"
85 | "vector_l2_ops"
86 | "vector_ip_ops"
87 | "halfvec_cosine_ops"
88 | "halfvec_l2_ops"
89 | "halfvec_ip_ops"
90 | "sq8_cosine_ops"
91 | "sq8_l2_ops"
92 | "sq8_ip_ops"
93 | "gin_trgm_ops"
99 | "gist_trgm_ops"
100 | "text_pattern_ops"
105 | "varchar_pattern_ops"
106 | "bpchar_pattern_ops"
107 | "int4_ops"
108 | "int8_ops"
109 | "text_ops"
110 )
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct ParseError {
115 pub message: String,
116 pub token_pos: usize,
118}
119
120impl fmt::Display for ParseError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(
123 f,
124 "parse error at token #{}: {}",
125 self.token_pos, self.message
126 )
127 }
128}
129
130impl From<LexError> for ParseError {
131 fn from(e: LexError) -> Self {
132 Self {
133 message: format!("lex: {e}"),
134 token_pos: 0,
135 }
136 }
137}
138
139pub fn parse_expression(input: &str) -> Result<Expr, ParseError> {
145 let tokens = lexer::tokenize(input)?;
146 let mut p = Parser::new(tokens);
147 let expr = p.parse_expr(0)?;
148 p.expect_eof()?;
149 Ok(expr)
150}
151
152pub fn parse_statement(input: &str) -> Result<Statement, ParseError> {
155 parse_statement_with(input, false)
156}
157
158pub fn parse_statement_with(input: &str, backslash_escapes: bool) -> Result<Statement, ParseError> {
162 let tokens = lexer::tokenize_with(input, backslash_escapes)?;
163 let mut p = Parser::new(tokens);
164 let stmt = p.parse_one_statement()?;
165 if matches!(p.peek(), Token::Semicolon) {
166 p.advance();
167 }
168 p.expect_eof()?;
169 Ok(stmt)
170}
171
172struct Parser {
173 tokens: Vec<Token>,
174 pos: usize,
175}
176
177enum NamedTableConstraintKind {
181 Check,
182 Unique,
183 PrimaryKey,
184}
185
186impl Parser {
187 fn new(tokens: Vec<Token>) -> Self {
188 Self { tokens, pos: 0 }
189 }
190
191 fn peek(&self) -> &Token {
192 &self.tokens[self.pos]
194 }
195
196 fn advance(&mut self) -> Token {
197 let t = mem::replace(&mut self.tokens[self.pos], Token::Eof);
198 if self.pos + 1 < self.tokens.len() {
199 self.pos += 1;
200 }
201 t
202 }
203
204 fn err(&self, message: String) -> ParseError {
205 ParseError {
206 message,
207 token_pos: self.pos,
208 }
209 }
210
211 fn expect_eof(&self) -> Result<(), ParseError> {
212 if matches!(self.peek(), Token::Eof) {
213 Ok(())
214 } else {
215 Err(self.err(format!("expected end of input, got {:?}", self.peek())))
216 }
217 }
218
219 fn consume_until_statement_boundary(&mut self) {
224 loop {
225 match self.peek() {
226 Token::Semicolon | Token::Eof => return,
227 _ => self.advance(),
228 };
229 }
230 }
231
232 fn scan_sequence_name_until_boundary(&mut self) -> Option<String> {
238 let mut seq: Option<String> = None;
239 let mut after_sequence_kw = false;
240 let mut after_name_kw = false;
241 loop {
242 match self.peek().clone() {
243 Token::Semicolon | Token::Eof => break,
244 Token::Ident(s) | Token::QuotedIdent(s) => {
245 if after_name_kw && seq.is_none() {
246 self.advance();
247 let mut name = s;
248 while matches!(self.peek(), Token::Dot) {
251 self.advance();
252 if let Token::Ident(n) | Token::QuotedIdent(n) = self.advance() {
253 name = n;
254 }
255 }
256 seq = Some(name);
257 after_name_kw = false;
258 continue;
259 }
260 if after_sequence_kw && s.eq_ignore_ascii_case("name") {
261 after_name_kw = true;
262 after_sequence_kw = false;
263 } else {
264 after_sequence_kw = s.eq_ignore_ascii_case("sequence");
265 }
266 self.advance();
267 }
268 Token::String(s) => {
269 if seq.is_none() {
270 let bare = s
272 .rsplit_once('.')
273 .map_or_else(|| s.clone(), |(_, b)| b.to_string());
274 seq = Some(bare);
275 }
276 self.advance();
277 }
278 _ => {
279 after_sequence_kw = false;
280 after_name_kw = false;
281 self.advance();
282 }
283 }
284 }
285 seq
286 }
287
288 fn expect_ident_like(&mut self) -> Result<String, ParseError> {
289 let first = match self.advance() {
290 Token::Ident(s) | Token::QuotedIdent(s) => s,
291 other => {
292 return Err(ParseError {
293 message: format!("expected identifier, got {other:?}"),
294 token_pos: self.pos.saturating_sub(1),
295 });
296 }
297 };
298 if matches!(self.peek(), Token::Dot) {
305 self.advance();
306 match self.advance() {
307 Token::Ident(s) | Token::QuotedIdent(s) => return Ok(s),
308 other => {
309 return Err(ParseError {
310 message: format!("expected identifier after '{first}.', got {other:?}"),
311 token_pos: self.pos.saturating_sub(1),
312 });
313 }
314 }
315 }
316 Ok(first)
317 }
318
319 #[allow(clippy::too_many_lines)]
320 fn parse_one_statement(&mut self) -> Result<Statement, ParseError> {
321 if matches!(self.peek(), Token::Eof | Token::Semicolon) {
329 return Ok(Statement::Empty);
330 }
331 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
341 let lc = s.to_ascii_lowercase();
342 if is_dump_noise_statement(&lc) {
343 self.consume_until_statement_boundary();
344 return Ok(Statement::Empty);
345 }
346 }
347 match self.peek() {
348 Token::Select => self.parse_select_stmt(),
349 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
365 self.advance();
366 let body_text = match self.advance() {
367 Token::String(s) => s,
368 other => {
369 return Err(self.err(alloc::format!(
370 "expected dollar-quoted body after DO, got {other:?}"
371 )));
372 }
373 };
374 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("language")) {
376 self.advance();
377 let _ = self.expect_ident_like()?;
378 }
379 let block = parse_plpgsql_body(&body_text)?;
384 Ok(Statement::DoBlock(block))
385 }
386 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with") => {
390 self.advance();
391 self.parse_with_cte_then_select()
392 }
393 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("explain") => {
396 self.advance();
397 let mut analyze = false;
398 let mut suggest = false;
399 if matches!(self.peek(), Token::LParen) {
401 self.advance();
402 let opt = match self.peek().clone() {
403 Token::Ident(s) | Token::QuotedIdent(s) => s,
404 other => {
405 return Err(self.err(format!(
406 "expected option keyword inside EXPLAIN (…), got {other:?}"
407 )));
408 }
409 };
410 if !opt.eq_ignore_ascii_case("suggest") {
411 return Err(self.err(format!(
412 "unknown EXPLAIN option {opt:?}; v6.8.3 supports SUGGEST"
413 )));
414 }
415 self.advance();
416 if !matches!(self.peek(), Token::RParen) {
417 return Err(self.err(format!(
418 "expected ')' after EXPLAIN option, got {:?}",
419 self.peek()
420 )));
421 }
422 self.advance();
423 suggest = true;
424 } else if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
425 && (s.eq_ignore_ascii_case("analyze") || s.eq_ignore_ascii_case("analyse"))
426 {
427 self.advance();
428 analyze = true;
429 }
430 let inner = self.parse_select_stmt()?;
431 let Statement::Select(s) = inner else {
432 return Err(self.err(format!("EXPLAIN body must be a SELECT, got {inner:?}")));
433 };
434 Ok(Statement::Explain(crate::ast::ExplainStatement {
435 analyze,
436 inner: Box::new(s),
437 suggest,
438 }))
439 }
440 Token::Create => self.parse_create_stmt(),
441 Token::Insert => self.parse_insert_stmt(),
442 Token::Begin => {
443 self.advance();
444 Ok(Statement::Begin)
445 }
446 Token::Commit => {
447 self.advance();
448 Ok(Statement::Commit)
449 }
450 Token::Rollback => {
451 self.advance();
452 if matches!(self.peek(), Token::To) {
456 self.advance();
457 if matches!(self.peek(), Token::Savepoint) {
458 self.advance();
459 }
460 let name = self.expect_ident_like()?;
461 Ok(Statement::RollbackToSavepoint(name))
462 } else {
463 Ok(Statement::Rollback)
464 }
465 }
466 Token::Savepoint => {
467 self.advance();
468 let name = self.expect_ident_like()?;
469 Ok(Statement::Savepoint(name))
470 }
471 Token::Release => {
472 self.advance();
473 if matches!(self.peek(), Token::Savepoint) {
476 self.advance();
477 }
478 let name = self.expect_ident_like()?;
479 Ok(Statement::ReleaseSavepoint(name))
480 }
481 Token::Show => {
482 self.advance();
483 let target = match self.advance() {
489 Token::Tables => "tables".to_string(),
490 Token::Create => "create".to_string(),
494 Token::Index => "index".to_string(),
497 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
498 other => {
499 return Err(self.err(format!(
500 "expected SHOW target, got {other:?}"
501 )));
502 }
503 };
504 match target.as_str() {
505 "tables" => Ok(Statement::ShowTables),
506 "users" => Ok(Statement::ShowUsers),
507 "indexes" | "index" | "keys" => {
517 if !matches!(self.peek(), Token::From) {
518 return Err(self.err(format!(
519 "expected FROM after SHOW INDEXES, got {:?}",
520 self.peek()
521 )));
522 }
523 self.advance();
524 let table = self.expect_ident_like()?;
525 Ok(Statement::ShowIndexes(table))
526 }
527 "status" => Ok(Statement::ShowStatus),
532 "variables" => Ok(Statement::ShowVariables),
533 "processlist" => Ok(Statement::ShowProcesslist),
535 "create" => {
536 let kind = match self.advance() {
539 Token::Ident(s) | Token::QuotedIdent(s) => s,
540 Token::Table => "table".to_string(),
541 other => {
542 return Err(self.err(format!(
543 "expected TABLE after SHOW CREATE, got {other:?}"
544 )));
545 }
546 };
547 if !kind.eq_ignore_ascii_case("table") {
548 return Err(self.err(format!(
549 "unsupported SHOW CREATE {kind:?}; v7.17 supports TABLE only"
550 )));
551 }
552 let name = self.expect_ident_like()?;
553 Ok(Statement::ShowCreateTable(name))
554 }
555 "databases" | "schemas" => Ok(Statement::ShowDatabases),
561 "publications" => Ok(Statement::ShowPublications),
566 "subscriptions" => Ok(Statement::ShowSubscriptions),
568 "columns" => {
569 if !matches!(self.peek(), Token::From) {
570 return Err(self.err(format!(
571 "expected FROM after SHOW COLUMNS, got {:?}",
572 self.peek()
573 )));
574 }
575 self.advance();
576 let table = self.expect_ident_like()?;
577 Ok(Statement::ShowColumns(table))
578 }
579 other => Err(self.err(format!(
580 "unknown SHOW target {other:?}; supported: TABLES, COLUMNS, USERS, PUBLICATIONS"
581 ))),
582 }
583 }
584 Token::Drop => {
590 self.advance();
591 match self.peek() {
592 Token::Publication => {
593 self.advance();
594 let name = self.expect_ident_or_string()?;
595 Ok(Statement::DropPublication(name))
596 }
597 Token::Subscription => {
598 self.advance();
599 let name = self.expect_ident_or_string()?;
600 Ok(Statement::DropSubscription(name))
601 }
602 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
603 self.advance();
604 let name = self.expect_ident_or_string()?;
605 Ok(Statement::DropUser(name))
606 }
607 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
609 self.advance();
610 let if_exists = self.consume_if_exists();
611 let name = self.expect_ident_like()?;
612 if !matches!(self.peek(), Token::On) {
614 return Err(self.err(alloc::format!(
615 "expected ON <table> after DROP TRIGGER {name:?}, got {:?}",
616 self.peek()
617 )));
618 }
619 self.advance();
620 let table = self.expect_ident_like()?;
621 Ok(Statement::DropTrigger {
622 name,
623 table,
624 if_exists,
625 })
626 }
627 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
631 self.advance();
632 let if_exists = self.consume_if_exists();
633 let name = self.expect_ident_like()?;
634 if matches!(self.peek(), Token::LParen) {
636 self.advance();
637 let mut depth = 1usize;
639 while depth > 0 {
640 match self.peek() {
641 Token::LParen => depth += 1,
642 Token::RParen => depth -= 1,
643 Token::Eof => {
644 return Err(self.err(alloc::format!(
645 "unterminated arg list in DROP FUNCTION {name:?}"
646 )));
647 }
648 _ => {}
649 }
650 self.advance();
651 }
652 }
653 Ok(Statement::DropFunction { name, if_exists })
654 }
655 Token::Table => {
663 self.advance();
664 let if_exists = self.consume_if_exists();
665 let mut names: Vec<String> = Vec::new();
666 loop {
667 names.push(self.expect_ident_like()?);
668 if matches!(self.peek(), Token::Comma) {
669 self.advance();
670 continue;
671 }
672 break;
673 }
674 if matches!(
675 self.peek(),
676 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
677 || s.eq_ignore_ascii_case("restrict")
678 ) {
679 self.advance();
680 }
681 Ok(Statement::DropTable { names, if_exists })
682 }
683 Token::Index => {
689 self.advance();
690 let if_exists = self.consume_if_exists();
691 let name = self.expect_ident_like()?;
692 if matches!(
693 self.peek(),
694 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
695 || s.eq_ignore_ascii_case("restrict")
696 ) {
697 self.advance();
698 }
699 Ok(Statement::DropIndex { name, if_exists })
700 }
701 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
707 self.advance();
708 let if_exists = self.consume_if_exists();
709 let mut names = vec![self.expect_ident_like()?];
710 while matches!(self.peek(), Token::Comma) {
711 self.advance();
712 names.push(self.expect_ident_like()?);
713 }
714 if matches!(
715 self.peek(),
716 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
717 || s.eq_ignore_ascii_case("restrict")
718 ) {
719 self.advance();
720 }
721 Ok(Statement::DropSchema { names, if_exists })
722 }
723 Token::Ident(s) | Token::QuotedIdent(s)
726 if s.eq_ignore_ascii_case("type") =>
727 {
728 self.advance();
729 let if_exists = self.consume_if_exists();
730 let mut names = vec![self.expect_ident_like()?];
731 while matches!(self.peek(), Token::Comma) {
732 self.advance();
733 names.push(self.expect_ident_like()?);
734 }
735 if matches!(
736 self.peek(),
737 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
738 || s.eq_ignore_ascii_case("restrict")
739 ) {
740 self.advance();
741 }
742 Ok(Statement::DropType { names, if_exists })
743 }
744 Token::Ident(s) | Token::QuotedIdent(s)
747 if s.eq_ignore_ascii_case("domain") =>
748 {
749 self.advance();
750 let if_exists = self.consume_if_exists();
751 let mut names = vec![self.expect_ident_like()?];
752 while matches!(self.peek(), Token::Comma) {
753 self.advance();
754 names.push(self.expect_ident_like()?);
755 }
756 if matches!(
757 self.peek(),
758 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
759 || s.eq_ignore_ascii_case("restrict")
760 ) {
761 self.advance();
762 }
763 Ok(Statement::DropDomain { names, if_exists })
764 }
765 Token::Ident(s) | Token::QuotedIdent(s)
768 if s.eq_ignore_ascii_case("materialized") =>
769 {
770 self.advance();
771 let nxt = self.peek().clone();
772 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
773 {
774 return Err(self.err(alloc::format!(
775 "expected VIEW after DROP MATERIALIZED, got {nxt:?}"
776 )));
777 }
778 self.advance();
779 let if_exists = self.consume_if_exists();
780 let mut names = vec![self.expect_ident_like()?];
781 while matches!(self.peek(), Token::Comma) {
782 self.advance();
783 names.push(self.expect_ident_like()?);
784 }
785 if matches!(
786 self.peek(),
787 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
788 || s.eq_ignore_ascii_case("restrict")
789 ) {
790 self.advance();
791 }
792 Ok(Statement::DropMaterializedView { names, if_exists })
793 }
794 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
797 self.advance();
798 let if_exists = self.consume_if_exists();
799 let mut names = vec![self.expect_ident_like()?];
800 while matches!(self.peek(), Token::Comma) {
801 self.advance();
802 names.push(self.expect_ident_like()?);
803 }
804 if matches!(
805 self.peek(),
806 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
807 || s.eq_ignore_ascii_case("restrict")
808 ) {
809 self.advance();
810 }
811 Ok(Statement::DropView { names, if_exists })
812 }
813 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
817 self.advance();
818 let if_exists = self.consume_if_exists();
819 let mut names = vec![self.expect_ident_like()?];
820 while matches!(self.peek(), Token::Comma) {
821 self.advance();
822 names.push(self.expect_ident_like()?);
823 }
824 if matches!(
825 self.peek(),
826 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
827 || s.eq_ignore_ascii_case("restrict")
828 ) {
829 self.advance();
830 }
831 Ok(Statement::DropSequence { names, if_exists })
832 }
833 other => Err(self.err(format!(
834 "expected TABLE / INDEX / SCHEMA / SEQUENCE / USER / PUBLICATION / \
835 SUBSCRIPTION / TRIGGER / FUNCTION after DROP, got {other:?}"
836 ))),
837 }
838 }
839 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("refresh") => {
841 self.advance();
842 let nxt = self.peek().clone();
843 if !matches!(&nxt, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("materialized"))
844 {
845 return Err(self.err(alloc::format!(
846 "expected MATERIALIZED after REFRESH, got {nxt:?}"
847 )));
848 }
849 self.advance();
850 let nxt2 = self.peek().clone();
851 if !matches!(&nxt2, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
852 {
853 return Err(self.err(alloc::format!(
854 "expected VIEW after REFRESH MATERIALIZED, got {nxt2:?}"
855 )));
856 }
857 self.advance();
858 let name = self.expect_ident_like()?;
859 let with_data = self.parse_optional_with_data(true)?;
860 Ok(Statement::RefreshMaterializedView { name, with_data })
861 }
862 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
863 self.advance();
864 self.parse_update_after_keyword()
865 }
866 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
867 self.advance();
868 self.parse_delete_after_keyword()
869 }
870 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("alter") => {
874 self.advance();
875 self.parse_alter_after_keyword()
876 }
877 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("wait") => {
881 self.advance();
882 self.parse_wait_after_keyword()
883 }
884 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("compact") => {
895 self.advance();
896 let next = self.peek().clone();
897 let cold = match next {
898 Token::Ident(s) | Token::QuotedIdent(s) => s,
899 _ => {
900 return Err(
901 self.err(format!("expected COLD after COMPACT, got {:?}", self.peek()))
902 );
903 }
904 };
905 if !cold.eq_ignore_ascii_case("cold") {
906 return Err(self.err(format!("expected COLD after COMPACT, got {cold:?}")));
907 }
908 self.advance();
909 let next = self.peek().clone();
910 let segments = match next {
911 Token::Ident(s) | Token::QuotedIdent(s) => s,
912 _ => {
913 return Err(self.err(format!(
914 "expected SEGMENTS after COMPACT COLD, got {:?}",
915 self.peek()
916 )));
917 }
918 };
919 if !segments.eq_ignore_ascii_case("segments") {
920 return Err(self.err(format!(
921 "expected SEGMENTS after COMPACT COLD, got {segments:?}"
922 )));
923 }
924 self.advance();
925 Ok(Statement::CompactColdSegments)
926 }
927 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("merge") => {
934 self.advance();
935 self.parse_merge_after_keyword()
936 }
937 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("analyze") => {
938 self.advance();
939 let target = match self.peek() {
940 Token::Eof | Token::Semicolon => None,
941 Token::Ident(_) | Token::QuotedIdent(_) => {
942 Some(self.expect_ident_like()?)
943 }
944 other => {
945 return Err(self.err(format!(
946 "expected table name or end of statement after ANALYZE, got {other:?}"
947 )));
948 }
949 };
950 Ok(Statement::Analyze(target))
951 }
952 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {
958 self.advance();
959 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("local") || s.eq_ignore_ascii_case("session") || s.eq_ignore_ascii_case("global"))
964 {
965 self.advance();
966 }
967 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("names"))
972 {
973 self.advance();
974 if matches!(
976 self.peek(),
977 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
978 ) {
979 self.advance();
980 }
981 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate"))
983 {
984 self.advance();
985 if matches!(
986 self.peek(),
987 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
988 ) {
989 self.advance();
990 }
991 }
992 return Ok(Statement::Empty);
993 }
994 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("authorization"))
1004 {
1005 self.advance(); match self.peek().clone() {
1007 Token::Default => {
1008 self.advance();
1009 }
1010 Token::String(_)
1011 | Token::Ident(_)
1012 | Token::QuotedIdent(_) => {
1013 self.advance();
1014 }
1015 other => {
1016 return Err(self.err(alloc::format!(
1017 "expected DEFAULT / '<role>' / <ident> after SET SESSION AUTHORIZATION, got {other:?}"
1018 )));
1019 }
1020 }
1021 return Ok(Statement::Empty);
1022 }
1023 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
1026 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
1027 {
1028 self.advance(); self.advance(); if matches!(
1031 self.peek(),
1032 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
1033 ) {
1034 self.advance();
1035 }
1036 return Ok(Statement::Empty);
1037 }
1038 let mut pairs: Vec<(String, crate::ast::SetValue)> = Vec::new();
1043 loop {
1044 let lhs = match self.peek().clone() {
1045 Token::SessionVar(s) => {
1046 self.advance();
1047 s
1048 }
1049 Token::Ident(_) | Token::QuotedIdent(_) => self.parse_set_param_name()?,
1050 other => {
1051 return Err(self.err(format!(
1052 "expected parameter name after SET, got {other:?}"
1053 )));
1054 }
1055 };
1056 match self.peek() {
1058 Token::Eq => {
1059 self.advance();
1060 }
1061 Token::To => {
1062 self.advance();
1063 }
1064 other => {
1065 return Err(self.err(format!(
1066 "expected `=` or TO after SET {lhs}, got {other:?}"
1067 )));
1068 }
1069 }
1070 let value = self.parse_set_value()?;
1071 pairs.push((lhs, value));
1072 if matches!(self.peek(), Token::Comma) {
1073 self.advance();
1074 continue;
1075 }
1076 break;
1077 }
1078 if pairs.len() == 1 {
1079 let (name, value) = pairs.into_iter().next().unwrap();
1080 Ok(Statement::SetParameter { name, value })
1081 } else {
1082 Ok(Statement::SetParameterList(pairs))
1083 }
1084 }
1085 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("reset") => {
1087 self.advance();
1088 match self.peek().clone() {
1089 Token::All => {
1090 self.advance();
1091 Ok(Statement::ResetParameter(None))
1092 }
1093 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("all") => {
1094 self.advance();
1095 Ok(Statement::ResetParameter(None))
1096 }
1097 _ => {
1098 let name = self.parse_set_param_name()?;
1099 Ok(Statement::ResetParameter(Some(name)))
1100 }
1101 }
1102 }
1103 other => Err(self.err(format!(
1104 "expected SELECT / CREATE / DROP / INSERT / UPDATE / DELETE / ALTER / BEGIN / COMMIT / \
1105 ROLLBACK / SAVEPOINT / RELEASE / SHOW at start of statement, got {other:?}"
1106 ))),
1107 }
1108 }
1109
1110 fn parse_create_stmt(&mut self) -> Result<Statement, ParseError> {
1111 debug_assert!(matches!(self.peek(), Token::Create));
1112 self.advance();
1113 match self.peek() {
1114 Token::Table => self.parse_create_table_stmt_after_create(),
1115 Token::Index => self.parse_create_index_stmt_after_create(false),
1116 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unique") => {
1123 self.advance();
1124 if !matches!(self.peek(), Token::Index) {
1125 return Err(self.err(alloc::format!(
1126 "expected INDEX after CREATE UNIQUE, got {:?}",
1127 self.peek()
1128 )));
1129 }
1130 self.parse_create_index_stmt_after_create(true)
1131 }
1132 Token::Publication => {
1133 self.advance();
1134 self.parse_create_publication_after_keyword()
1135 }
1136 Token::Subscription => {
1137 self.advance();
1138 self.parse_create_subscription_after_keyword()
1139 }
1140 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("user") => {
1144 self.advance();
1145 self.parse_create_user_after_keyword()
1146 }
1147 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("extension") => {
1151 self.advance();
1152 self.parse_create_extension_after_keyword()
1153 }
1154 Token::Or => {
1160 self.advance();
1161 let next = self.peek();
1162 let (Token::Ident(s2) | Token::QuotedIdent(s2)) = next else {
1163 return Err(self.err(alloc::format!(
1164 "expected REPLACE after CREATE OR, got {next:?}"
1165 )));
1166 };
1167 if !s2.eq_ignore_ascii_case("replace") {
1168 return Err(self.err(alloc::format!(
1169 "expected REPLACE after CREATE OR, got {s2:?}"
1170 )));
1171 }
1172 self.advance();
1173 self.parse_create_function_or_trigger_after_or_replace(true)
1174 }
1175 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("function") => {
1176 self.advance();
1177 self.parse_create_function_after_keyword(false)
1178 }
1179 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("trigger") => {
1180 self.advance();
1181 self.parse_create_trigger_after_keyword(false)
1182 }
1183 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
1185 self.advance();
1186 self.parse_create_sequence_after_keyword(false)
1187 }
1188 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("view") => {
1190 self.advance();
1191 self.parse_create_view_after_keyword(false, false, false)
1192 }
1193 Token::Ident(s) | Token::QuotedIdent(s)
1207 if s.eq_ignore_ascii_case("algorithm")
1208 || s.eq_ignore_ascii_case("definer")
1209 || s.eq_ignore_ascii_case("sql") =>
1210 {
1211 self.consume_mysql_view_prefix()?;
1212 let next = self.peek().clone();
1217 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2)
1218 if s2.eq_ignore_ascii_case("view"))
1219 {
1220 self.advance();
1221 self.parse_create_view_after_keyword(false, false, false)
1222 } else {
1223 Err(self.err(alloc::format!(
1224 "expected VIEW after MySQL view prefix (ALGORITHM/DEFINER/SQL SECURITY), got {next:?}"
1225 )))
1226 }
1227 }
1228 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("type") => {
1230 self.advance();
1231 self.parse_create_type_after_keyword()
1232 }
1233 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("domain") => {
1236 self.advance();
1237 self.parse_create_domain_after_keyword()
1238 }
1239 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("schema") => {
1243 self.advance();
1244 let if_not_exists = self.parse_if_not_exists();
1245 let name = self.expect_ident_like()?;
1246 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
1249 if s.eq_ignore_ascii_case("authorization"))
1250 {
1251 self.advance();
1252 let _ = self.expect_ident_like()?;
1253 }
1254 Ok(Statement::CreateSchema { name, if_not_exists })
1255 }
1256 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("materialized") => {
1258 self.advance();
1259 let next = self.peek().clone();
1260 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1261 {
1262 self.advance();
1263 self.parse_create_materialized_view_after_keyword()
1264 } else {
1265 Err(self.err(alloc::format!(
1266 "expected VIEW after CREATE MATERIALIZED, got {next:?}"
1267 )))
1268 }
1269 }
1270 Token::Ident(s) | Token::QuotedIdent(s)
1271 if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") =>
1272 {
1273 self.advance();
1274 let next = self.peek().clone();
1276 if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("sequence"))
1277 {
1278 self.advance();
1279 self.parse_create_sequence_after_keyword(true)
1280 } else if matches!(&next, Token::Ident(s2) | Token::QuotedIdent(s2) if s2.eq_ignore_ascii_case("view"))
1281 {
1282 self.advance();
1283 self.parse_create_view_after_keyword(false, false, true)
1284 } else {
1285 self.consume_until_statement_boundary();
1287 Ok(Statement::Empty)
1288 }
1289 }
1290 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("procedure") => {
1300 self.consume_mysql_routine_body();
1301 Ok(Statement::Empty)
1302 }
1303 Token::Ident(s) | Token::QuotedIdent(s)
1314 if matches!(
1315 s.to_ascii_lowercase().as_str(),
1316 "database"
1317 | "role"
1318 | "policy"
1319 | "operator"
1320 | "cast"
1321 | "rule"
1322 | "aggregate"
1323 | "language"
1324 | "collation"
1325 | "conversion"
1326 | "statistics"
1334 | "event"
1335 | "foreign"
1336 ) =>
1337 {
1338 self.consume_until_statement_boundary();
1339 Ok(Statement::Empty)
1340 }
1341 other => Err(self.err(format!(
1342 "expected TABLE / INDEX / USER / EXTENSION / PUBLICATION / SUBSCRIPTION / FUNCTION / TRIGGER / SEQUENCE / SCHEMA / VIEW / TYPE / DOMAIN [OR REPLACE …] after CREATE, got {other:?}"
1343 ))),
1344 }
1345 }
1346
1347 fn parse_create_function_or_trigger_after_or_replace(
1352 &mut self,
1353 or_replace: bool,
1354 ) -> Result<Statement, ParseError> {
1355 let tok = self.peek();
1356 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1357 return Err(self.err(alloc::format!(
1358 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {tok:?}"
1359 )));
1360 };
1361 if s.eq_ignore_ascii_case("function") {
1362 self.advance();
1363 self.parse_create_function_after_keyword(or_replace)
1364 } else if s.eq_ignore_ascii_case("trigger") {
1365 self.advance();
1366 self.parse_create_trigger_after_keyword(or_replace)
1367 } else if s.eq_ignore_ascii_case("view") {
1368 self.advance();
1370 self.parse_create_view_after_keyword(or_replace, false, false)
1371 } else if s.eq_ignore_ascii_case("temporary") || s.eq_ignore_ascii_case("temp") {
1372 self.advance();
1374 let nxt = self.peek().clone();
1375 if matches!(&nxt, Token::Ident(n) | Token::QuotedIdent(n) if n.eq_ignore_ascii_case("view"))
1376 {
1377 self.advance();
1378 self.parse_create_view_after_keyword(or_replace, false, true)
1379 } else {
1380 Err(self.err(alloc::format!(
1381 "expected VIEW after CREATE OR REPLACE TEMPORARY, got {nxt:?}"
1382 )))
1383 }
1384 } else {
1385 Err(self.err(alloc::format!(
1386 "expected FUNCTION / TRIGGER / VIEW after CREATE OR REPLACE, got {s:?}"
1387 )))
1388 }
1389 }
1390
1391 fn parse_create_extension_after_keyword(&mut self) -> Result<Statement, ParseError> {
1396 self.consume_if_not_exists();
1398 let name = self.expect_ident_like()?;
1399 loop {
1402 match self.peek() {
1403 Token::Ident(s) if s.eq_ignore_ascii_case("with") => {
1404 self.advance();
1405 continue;
1406 }
1407 Token::Ident(s) if s.eq_ignore_ascii_case("schema") => {
1408 self.advance();
1409 let _ = self.expect_ident_like()?;
1410 continue;
1411 }
1412 Token::Ident(s) if s.eq_ignore_ascii_case("version") => {
1413 self.advance();
1414 let _ = self.advance();
1416 continue;
1417 }
1418 Token::Ident(s) if s.eq_ignore_ascii_case("from") => {
1419 self.advance();
1420 let _ = self.advance();
1421 continue;
1422 }
1423 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => {
1424 self.advance();
1425 continue;
1426 }
1427 _ => break,
1428 }
1429 }
1430 Ok(Statement::CreateExtension(name))
1431 }
1432
1433 fn parse_create_function_after_keyword(
1445 &mut self,
1446 or_replace: bool,
1447 ) -> Result<Statement, ParseError> {
1448 let name = self.expect_ident_like()?;
1449 if !matches!(self.peek(), Token::LParen) {
1453 return Err(self.err(alloc::format!(
1454 "expected '(' after function name {name:?}, got {:?}",
1455 self.peek()
1456 )));
1457 }
1458 self.advance();
1459 let args = self.parse_function_arg_list()?;
1460 let tok = self.peek();
1462 let (Token::Ident(s) | Token::QuotedIdent(s)) = tok else {
1463 return Err(self.err(alloc::format!(
1464 "expected RETURNS after function arg list, got {tok:?}"
1465 )));
1466 };
1467 if !s.eq_ignore_ascii_case("returns") {
1468 return Err(self.err(alloc::format!(
1469 "expected RETURNS after function arg list, got {s:?}"
1470 )));
1471 }
1472 self.advance();
1473 let returns = self.parse_function_return()?;
1474 let mut language: Option<String> = self.parse_optional_language()?;
1477 if !matches!(self.peek(), Token::As) {
1481 return Err(self.err(alloc::format!(
1482 "expected AS before function body, got {:?}",
1483 self.peek()
1484 )));
1485 }
1486 self.advance();
1487 let body_text = match self.peek() {
1488 Token::String(s) => {
1489 let body = s.clone();
1490 self.advance();
1491 body
1492 }
1493 other => {
1494 return Err(self.err(alloc::format!(
1495 "expected $$-quoted function body after AS, got {other:?}"
1496 )));
1497 }
1498 };
1499 if language.is_none() {
1501 language = self.parse_optional_language()?;
1502 }
1503 let language = language.unwrap_or_else(|| String::from("sql"));
1504 let body = if language.eq_ignore_ascii_case("plpgsql") {
1509 match parse_plpgsql_body(&body_text) {
1510 Ok(block) => FunctionBody::PlPgSql(block),
1511 Err(_) => FunctionBody::Raw(body_text),
1517 }
1518 } else {
1519 FunctionBody::Raw(body_text)
1520 };
1521 Ok(Statement::CreateFunction(CreateFunctionStatement {
1522 name,
1523 or_replace,
1524 args,
1525 returns,
1526 language,
1527 body,
1528 }))
1529 }
1530
1531 fn parse_function_arg_list(&mut self) -> Result<Vec<FunctionArg>, ParseError> {
1535 let mut args: Vec<FunctionArg> = Vec::new();
1536 if matches!(self.peek(), Token::RParen) {
1537 self.advance();
1538 return Ok(args);
1539 }
1540 loop {
1541 let mode = if matches!(self.peek(), Token::In) {
1544 self.advance();
1545 FunctionArgMode::In
1546 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("out"))
1547 {
1548 self.advance();
1549 FunctionArgMode::Out
1550 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("inout"))
1551 {
1552 self.advance();
1553 FunctionArgMode::InOut
1554 } else {
1555 FunctionArgMode::In
1556 };
1557 let (name, ty_token) = {
1563 let first = self.expect_ident_like()?;
1564 match self.peek() {
1567 Token::Ident(_) | Token::QuotedIdent(_) => {
1568 let ty = self.expect_ident_like()?;
1569 (Some(first), ty)
1570 }
1571 _ => (None, first),
1572 }
1573 };
1574 let ty = match map_type_ident_to_column_type_name(&ty_token) {
1576 Some(t) => FunctionArgType::Typed(t),
1577 None => FunctionArgType::Raw(ty_token),
1578 };
1579 args.push(FunctionArg { mode, name, ty });
1580 match self.peek() {
1581 Token::Comma => {
1582 self.advance();
1583 continue;
1584 }
1585 Token::RParen => {
1586 self.advance();
1587 return Ok(args);
1588 }
1589 other => {
1590 return Err(self.err(alloc::format!(
1591 "expected , or ) in function arg list, got {other:?}"
1592 )));
1593 }
1594 }
1595 }
1596 }
1597
1598 fn parse_function_return(&mut self) -> Result<FunctionReturn, ParseError> {
1599 let ident = self.expect_ident_like()?;
1600 if ident.eq_ignore_ascii_case("trigger") {
1601 return Ok(FunctionReturn::Trigger);
1602 }
1603 if ident.eq_ignore_ascii_case("void") {
1604 return Ok(FunctionReturn::Void);
1605 }
1606 match map_type_ident_to_column_type_name(&ident) {
1607 Some(t) => Ok(FunctionReturn::Type(t)),
1608 None => Ok(FunctionReturn::Other(ident)),
1609 }
1610 }
1611
1612 fn parse_optional_language(&mut self) -> Result<Option<String>, ParseError> {
1613 match self.peek() {
1614 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("language") => {
1615 self.advance();
1616 let lang = self.expect_ident_like()?;
1617 Ok(Some(lang.to_ascii_lowercase()))
1618 }
1619 _ => Ok(None),
1620 }
1621 }
1622
1623 fn parse_create_domain_after_keyword(&mut self) -> Result<Statement, ParseError> {
1629 let name = self.expect_ident_like()?;
1630 if matches!(self.peek(), Token::As) {
1632 self.advance();
1633 }
1634 let base_type = self.parse_column_type_name()?;
1635 let mut default: Option<Expr> = None;
1636 let mut not_null = false;
1637 let mut checks: Vec<Expr> = Vec::new();
1638 loop {
1639 match self.peek() {
1640 Token::Default => {
1641 if default.is_some() {
1642 return Err(self.err("DOMAIN DEFAULT specified twice".into()));
1643 }
1644 self.advance();
1645 default = Some(self.parse_expr(0)?);
1646 }
1647 Token::Not => {
1648 self.advance();
1649 if !matches!(self.peek(), Token::Null) {
1650 return Err(self.err(alloc::format!(
1651 "expected NULL after NOT in DOMAIN, got {:?}",
1652 self.peek()
1653 )));
1654 }
1655 self.advance();
1656 not_null = true;
1657 }
1658 Token::Null => {
1659 self.advance();
1660 }
1664 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("check") => {
1665 self.advance();
1666 if !matches!(self.peek(), Token::LParen) {
1667 return Err(self.err(alloc::format!(
1668 "expected '(' after CHECK in DOMAIN, got {:?}",
1669 self.peek()
1670 )));
1671 }
1672 self.advance();
1673 let expr = self.parse_expr(0)?;
1674 if !matches!(self.peek(), Token::RParen) {
1675 return Err(self.err(alloc::format!(
1676 "expected ')' after CHECK expr, got {:?}",
1677 self.peek()
1678 )));
1679 }
1680 self.advance();
1681 checks.push(expr);
1682 }
1683 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("constraint") => {
1687 self.advance();
1688 let _ = self.expect_ident_like()?;
1689 }
1690 _ => break,
1691 }
1692 }
1693 Ok(Statement::CreateDomain(crate::ast::CreateDomainStatement {
1694 name,
1695 base_type,
1696 default,
1697 not_null,
1698 checks,
1699 }))
1700 }
1701
1702 fn parse_create_type_after_keyword(&mut self) -> Result<Statement, ParseError> {
1706 let name = self.expect_ident_like()?;
1707 if !matches!(self.peek(), Token::As) {
1709 return Err(self.err(alloc::format!(
1710 "expected AS after CREATE TYPE {name:?}, got {:?}",
1711 self.peek()
1712 )));
1713 }
1714 self.advance();
1715 let kind_ident = match self.peek().clone() {
1717 Token::Ident(s) | Token::QuotedIdent(s) => s,
1718 other => {
1719 return Err(self.err(alloc::format!(
1720 "expected ENUM after CREATE TYPE {name:?} AS, got {other:?}"
1721 )));
1722 }
1723 };
1724 if !kind_ident.eq_ignore_ascii_case("enum") {
1725 return Err(self.err(alloc::format!(
1726 "Phase 1.4 only supports ENUM; got {kind_ident:?}"
1727 )));
1728 }
1729 self.advance();
1730 if !matches!(self.peek(), Token::LParen) {
1731 return Err(self.err(alloc::format!(
1732 "expected '(' after ENUM, got {:?}",
1733 self.peek()
1734 )));
1735 }
1736 self.advance();
1737 let mut labels: Vec<String> = Vec::new();
1738 loop {
1739 match self.peek().clone() {
1740 Token::String(s) => {
1741 self.advance();
1742 labels.push(s);
1743 }
1744 other => {
1745 return Err(
1746 self.err(alloc::format!("expected enum label string, got {other:?}"))
1747 );
1748 }
1749 }
1750 if matches!(self.peek(), Token::Comma) {
1751 self.advance();
1752 continue;
1753 }
1754 if matches!(self.peek(), Token::RParen) {
1755 self.advance();
1756 break;
1757 }
1758 return Err(self.err(alloc::format!(
1759 "expected , or ) in ENUM label list, got {:?}",
1760 self.peek()
1761 )));
1762 }
1763 if labels.is_empty() {
1764 return Err(self.err("CREATE TYPE … AS ENUM must declare at least one label".into()));
1765 }
1766 Ok(Statement::CreateType(crate::ast::CreateTypeStatement {
1767 name,
1768 kind: crate::ast::TypeKind::Enum { labels },
1769 }))
1770 }
1771
1772 fn parse_create_materialized_view_after_keyword(&mut self) -> Result<Statement, ParseError> {
1777 let if_not_exists = self.parse_if_not_exists();
1778 let name = self.expect_ident_like()?;
1779 let mut columns: Vec<String> = Vec::new();
1780 if matches!(self.peek(), Token::LParen) {
1781 self.advance();
1782 loop {
1783 let c = self.expect_ident_like()?;
1784 columns.push(c);
1785 if matches!(self.peek(), Token::Comma) {
1786 self.advance();
1787 continue;
1788 }
1789 if matches!(self.peek(), Token::RParen) {
1790 self.advance();
1791 break;
1792 }
1793 return Err(self.err(alloc::format!(
1794 "expected , or ) in MATERIALIZED VIEW column list, got {:?}",
1795 self.peek()
1796 )));
1797 }
1798 }
1799 if !matches!(self.peek(), Token::As) {
1800 return Err(self.err(alloc::format!(
1801 "expected AS <SELECT …> after CREATE MATERIALIZED VIEW {name:?}, got {:?}",
1802 self.peek()
1803 )));
1804 }
1805 self.advance();
1806 let body_stmt = self.parse_select_stmt()?;
1807 let Statement::Select(body) = body_stmt else {
1808 return Err(self.err(alloc::format!(
1809 "CREATE MATERIALIZED VIEW body must be a SELECT, got {body_stmt:?}"
1810 )));
1811 };
1812 let with_data = self.parse_optional_with_data(true)?;
1814 Ok(Statement::CreateMaterializedView(
1815 crate::ast::CreateMaterializedViewStatement {
1816 name,
1817 if_not_exists,
1818 columns,
1819 body,
1820 with_data,
1821 },
1822 ))
1823 }
1824
1825 fn parse_optional_with_data(&mut self, default_when_absent: bool) -> Result<bool, ParseError> {
1830 let save = self.pos;
1831 let is_with = match self.peek() {
1833 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("with"),
1834 _ => false,
1835 };
1836 if !is_with {
1837 return Ok(default_when_absent);
1838 }
1839 self.advance();
1840 let mut with_data = true;
1842 let is_no = match self.peek() {
1843 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("no"),
1844 _ => false,
1845 };
1846 if is_no {
1847 self.advance();
1848 with_data = false;
1849 }
1850 let is_data = match self.peek() {
1852 Token::Ident(s) | Token::QuotedIdent(s) => s.eq_ignore_ascii_case("data"),
1853 _ => false,
1854 };
1855 if is_data {
1856 self.advance();
1857 Ok(with_data)
1858 } else {
1859 self.pos = save;
1862 Ok(default_when_absent)
1863 }
1864 }
1865
1866 fn parse_create_view_after_keyword(
1871 &mut self,
1872 or_replace: bool,
1873 _materialized_unused: bool,
1874 temporary: bool,
1875 ) -> Result<Statement, ParseError> {
1876 let if_not_exists = self.parse_if_not_exists();
1877 let name = self.expect_ident_like()?;
1878 let mut columns: Vec<String> = Vec::new();
1880 if matches!(self.peek(), Token::LParen) {
1881 self.advance();
1882 loop {
1883 let c = self.expect_ident_like()?;
1884 columns.push(c);
1885 if matches!(self.peek(), Token::Comma) {
1886 self.advance();
1887 continue;
1888 }
1889 if matches!(self.peek(), Token::RParen) {
1890 self.advance();
1891 break;
1892 }
1893 return Err(self.err(alloc::format!(
1894 "expected , or ) in VIEW column list, got {:?}",
1895 self.peek()
1896 )));
1897 }
1898 }
1899 if !matches!(self.peek(), Token::As) {
1901 return Err(self.err(alloc::format!(
1902 "expected AS <SELECT …> after CREATE VIEW {name:?}, got {:?}",
1903 self.peek()
1904 )));
1905 }
1906 self.advance();
1907 let body_stmt = self.parse_select_stmt()?;
1909 let Statement::Select(body) = body_stmt else {
1910 return Err(self.err(alloc::format!(
1911 "CREATE VIEW body must be a SELECT statement, got {body_stmt:?}"
1912 )));
1913 };
1914 Ok(Statement::CreateView(crate::ast::CreateViewStatement {
1915 name,
1916 or_replace,
1917 if_not_exists,
1918 temporary,
1919 columns,
1920 body,
1921 }))
1922 }
1923
1924 fn parse_create_sequence_after_keyword(
1928 &mut self,
1929 temporary: bool,
1930 ) -> Result<Statement, ParseError> {
1931 let if_not_exists = self.parse_if_not_exists();
1932 let name = self.expect_ident_like()?;
1933 let data_type = if matches!(self.peek(), Token::As) {
1935 self.advance();
1936 Some(self.parse_sequence_data_type()?)
1937 } else {
1938 None
1939 };
1940 let options = self.parse_sequence_options(false)?;
1941 Ok(Statement::CreateSequence(
1942 crate::ast::CreateSequenceStatement {
1943 name,
1944 if_not_exists,
1945 temporary,
1946 data_type,
1947 options,
1948 },
1949 ))
1950 }
1951
1952 fn parse_alter_sequence_after_keyword(&mut self) -> Result<Statement, ParseError> {
1955 let if_exists = self.parse_if_exists();
1956 let name = self.expect_ident_like()?;
1957 let options = self.parse_sequence_options(true)?;
1958 Ok(Statement::AlterSequence(
1959 crate::ast::AlterSequenceStatement {
1960 name,
1961 if_exists,
1962 options,
1963 },
1964 ))
1965 }
1966
1967 fn parse_sequence_data_type(&mut self) -> Result<crate::ast::SequenceDataType, ParseError> {
1968 let kw = self.expect_ident_like()?;
1969 match kw.to_ascii_lowercase().as_str() {
1970 "smallint" | "int2" => Ok(crate::ast::SequenceDataType::SmallInt),
1971 "integer" | "int" | "int4" => Ok(crate::ast::SequenceDataType::Int),
1972 "bigint" | "int8" => Ok(crate::ast::SequenceDataType::BigInt),
1973 other => Err(self.err(alloc::format!(
1974 "expected SMALLINT / INTEGER / BIGINT after SEQUENCE AS, got {other:?}"
1975 ))),
1976 }
1977 }
1978
1979 fn parse_sequence_options(
1980 &mut self,
1981 allow_restart: bool,
1982 ) -> Result<crate::ast::SequenceOptions, ParseError> {
1983 use crate::ast::{SeqBound, SequenceOptions, SequenceOwnedBy};
1984 let mut opts = SequenceOptions::default();
1985 #[allow(clippy::while_let_loop)]
1986 loop {
1987 let kw_lc = match self.peek() {
1990 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
1991 _ => break,
1992 };
1993 match kw_lc.as_str() {
1994 "increment" => {
1995 self.advance();
1996 if matches!(self.peek(), Token::By) {
1998 self.advance();
1999 }
2000 opts.increment = Some(self.expect_signed_int()?);
2001 }
2002 "minvalue" => {
2003 self.advance();
2004 opts.min_value = Some(SeqBound::Value(self.expect_signed_int()?));
2005 }
2006 "maxvalue" => {
2007 self.advance();
2008 opts.max_value = Some(SeqBound::Value(self.expect_signed_int()?));
2009 }
2010 "no" => {
2011 self.advance();
2012 let what = self.expect_ident_like()?;
2013 match what.to_ascii_lowercase().as_str() {
2014 "minvalue" => opts.min_value = Some(SeqBound::NoBound),
2015 "maxvalue" => opts.max_value = Some(SeqBound::NoBound),
2016 "cycle" => opts.cycle = Some(false),
2017 other => {
2018 return Err(self.err(alloc::format!(
2019 "expected MINVALUE / MAXVALUE / CYCLE after NO, got {other:?}"
2020 )));
2021 }
2022 }
2023 }
2024 "start" => {
2025 self.advance();
2026 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2028 if s.eq_ignore_ascii_case("with"))
2029 {
2030 self.advance();
2031 }
2032 opts.start = Some(self.expect_signed_int()?);
2033 }
2034 "restart" if allow_restart => {
2035 self.advance();
2036 let mut with_val: Option<i64> = None;
2038 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2039 if s.eq_ignore_ascii_case("with"))
2040 {
2041 self.advance();
2042 with_val = Some(self.expect_signed_int()?);
2043 } else if matches!(self.peek(), Token::Integer(_) | Token::Minus) {
2044 with_val = Some(self.expect_signed_int()?);
2045 }
2046 opts.restart = Some(with_val);
2047 }
2048 "cache" => {
2049 self.advance();
2050 opts.cache = Some(self.expect_signed_int()?);
2051 }
2052 "cycle" => {
2053 self.advance();
2054 opts.cycle = Some(true);
2055 }
2056 "owned" => {
2057 self.advance();
2058 match self.peek() {
2060 Token::By => {
2061 self.advance();
2062 }
2063 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("by") => {
2064 self.advance();
2065 }
2066 other => {
2067 return Err(
2068 self.err(alloc::format!("expected BY after OWNED, got {other:?}"))
2069 );
2070 }
2071 }
2072 let first = match self.advance() {
2076 Token::Ident(s) | Token::QuotedIdent(s) => s,
2077 other => {
2078 return Err(self.err(alloc::format!(
2079 "expected identifier or NONE after OWNED BY, got {other:?}"
2080 )));
2081 }
2082 };
2083 if first.eq_ignore_ascii_case("none") {
2084 opts.owned_by = Some(SequenceOwnedBy::None);
2085 } else if matches!(self.peek(), Token::Dot) {
2086 self.advance();
2087 let second = match self.advance() {
2088 Token::Ident(s) | Token::QuotedIdent(s) => s,
2089 other => {
2090 return Err(self.err(alloc::format!(
2091 "expected column name after OWNED BY {first}., got {other:?}"
2092 )));
2093 }
2094 };
2095 if matches!(self.peek(), Token::Dot) {
2104 self.advance();
2105 let third = match self.advance() {
2106 Token::Ident(s) | Token::QuotedIdent(s) => s,
2107 other => {
2108 return Err(self.err(alloc::format!(
2109 "expected column name after OWNED BY {first}.{second}., got {other:?}"
2110 )));
2111 }
2112 };
2113 let _ = first; opts.owned_by = Some(SequenceOwnedBy::Column {
2115 table: second,
2116 column: third,
2117 });
2118 } else {
2119 opts.owned_by = Some(SequenceOwnedBy::Column {
2120 table: first,
2121 column: second,
2122 });
2123 }
2124 } else {
2125 return Err(self.err(alloc::format!(
2126 "expected table.column or NONE after OWNED BY, got {first:?}"
2127 )));
2128 }
2129 }
2130 _ => break,
2131 }
2132 }
2133 Ok(opts)
2134 }
2135
2136 fn expect_signed_int(&mut self) -> Result<i64, ParseError> {
2137 let neg = if matches!(self.peek(), Token::Minus) {
2138 self.advance();
2139 true
2140 } else {
2141 false
2142 };
2143 match self.peek() {
2144 Token::Integer(n) => {
2145 let v = *n;
2146 self.advance();
2147 Ok(if neg { -v } else { v })
2148 }
2149 other => Err(self.err(alloc::format!("expected signed integer, got {other:?}"))),
2150 }
2151 }
2152
2153 fn consume_optional_deferrable_clauses(&mut self) -> Result<(), ParseError> {
2163 loop {
2164 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("deferrable")) {
2166 self.advance();
2167 self.consume_optional_initially_clause()?;
2168 continue;
2169 }
2170 if matches!(self.peek(), Token::Not) {
2172 let look = self.tokens.get(self.pos + 1);
2173 if matches!(look, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("deferrable")) {
2174 self.advance(); self.advance(); self.consume_optional_initially_clause()?;
2177 continue;
2178 }
2179 break;
2180 }
2181 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2186 self.consume_optional_initially_clause()?;
2187 continue;
2188 }
2189 break;
2190 }
2191 Ok(())
2192 }
2193
2194 fn consume_optional_initially_clause(&mut self) -> Result<(), ParseError> {
2198 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("initially")) {
2199 return Ok(());
2200 }
2201 self.advance(); match self.advance() {
2203 Token::Ident(s)
2204 if s.eq_ignore_ascii_case("deferred") || s.eq_ignore_ascii_case("immediate") =>
2205 {
2206 Ok(())
2207 }
2208 other => Err(self.err(alloc::format!(
2209 "expected DEFERRED or IMMEDIATE after INITIALLY, got {other:?}"
2210 ))),
2211 }
2212 }
2213
2214 fn consume_mysql_routine_body(&mut self) {
2232 let mut depth: i32 = 0;
2237 let mut started = false;
2238 loop {
2239 match self.peek().clone() {
2240 Token::Begin => {
2241 self.advance();
2242 depth += 1;
2243 started = true;
2244 }
2245 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2246 self.advance();
2247 if started {
2248 depth -= 1;
2249 if depth <= 0 {
2250 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2257 let is_inner_close = matches!(
2262 self.peek(),
2263 Token::Ident(s) | Token::QuotedIdent(s)
2264 if matches!(
2265 s.to_ascii_lowercase().as_str(),
2266 "if" | "loop" | "while" | "case" | "repeat"
2267 )
2268 );
2269 if is_inner_close {
2270 self.advance();
2271 depth += 1;
2272 continue;
2273 }
2274 }
2275 if matches!(self.peek(), Token::Semicolon) {
2277 self.advance();
2278 }
2279 return;
2280 }
2281 }
2282 }
2283 Token::Eof => return,
2284 _ => {
2285 self.advance();
2286 }
2287 }
2288 }
2289 }
2290
2291 fn consume_mysql_view_prefix(&mut self) -> Result<(), ParseError> {
2305 loop {
2306 match self.peek().clone() {
2307 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("algorithm") => {
2308 self.advance(); if matches!(self.peek(), Token::Eq) {
2312 self.advance();
2313 }
2314 if matches!(
2318 self.peek(),
2319 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2320 ) {
2321 self.advance();
2322 }
2323 }
2324 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("definer") => {
2325 self.advance(); if matches!(self.peek(), Token::Eq) {
2327 self.advance();
2328 }
2329 match self.peek().clone() {
2332 Token::String(_) | Token::Ident(_) | Token::QuotedIdent(_) => {
2333 self.advance();
2334 if matches!(self.peek(), Token::At) {
2336 self.advance();
2337 if matches!(
2338 self.peek(),
2339 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
2340 ) {
2341 self.advance();
2342 }
2343 }
2344 }
2345 _ => {}
2346 }
2347 }
2348 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sql") => {
2349 let save = self.pos;
2354 self.advance(); if matches!(self.peek(), Token::Ident(s2) | Token::QuotedIdent(s2)
2356 if s2.eq_ignore_ascii_case("security"))
2357 {
2358 self.advance(); if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
2361 self.advance();
2362 }
2363 } else {
2364 self.pos = save;
2367 return Ok(());
2368 }
2369 }
2370 _ => return Ok(()),
2371 }
2372 }
2373 }
2374
2375 fn parse_if_not_exists(&mut self) -> bool {
2376 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2377 {
2378 let save = self.pos;
2379 self.advance();
2380 if matches!(self.peek(), Token::Not) {
2381 self.advance();
2382 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2383 {
2384 self.advance();
2385 return true;
2386 }
2387 }
2388 self.pos = save;
2389 }
2390 false
2391 }
2392
2393 fn parse_if_exists(&mut self) -> bool {
2394 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2395 {
2396 let save = self.pos;
2397 self.advance();
2398 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists"))
2399 {
2400 self.advance();
2401 return true;
2402 }
2403 self.pos = save;
2404 }
2405 false
2406 }
2407
2408 fn parse_create_trigger_after_keyword(
2412 &mut self,
2413 or_replace: bool,
2414 ) -> Result<Statement, ParseError> {
2415 let name = self.expect_ident_like()?;
2416 let timing = {
2417 let ident = self.expect_ident_like()?;
2418 if ident.eq_ignore_ascii_case("before") {
2419 TriggerTiming::Before
2420 } else if ident.eq_ignore_ascii_case("after") {
2421 TriggerTiming::After
2422 } else if ident.eq_ignore_ascii_case("instead") {
2423 let next = self.expect_ident_like()?;
2424 if !next.eq_ignore_ascii_case("of") {
2425 return Err(self.err(alloc::format!(
2426 "expected OF after INSTEAD in trigger timing, got {next:?}"
2427 )));
2428 }
2429 TriggerTiming::InsteadOf
2430 } else {
2431 return Err(self.err(alloc::format!(
2432 "expected BEFORE / AFTER / INSTEAD OF in trigger timing, got {ident:?}"
2433 )));
2434 }
2435 };
2436 let mut events: Vec<TriggerEvent> = Vec::new();
2443 let mut update_columns: Vec<String> = Vec::new();
2444 let (first_ev, first_cols) = self.parse_trigger_event_with_optional_of()?;
2445 events.push(first_ev);
2446 if !first_cols.is_empty() {
2447 update_columns = first_cols;
2448 }
2449 while matches!(self.peek(), Token::Or) {
2450 self.advance();
2451 let (ev, cols) = self.parse_trigger_event_with_optional_of()?;
2452 events.push(ev);
2453 if !cols.is_empty() {
2454 if !update_columns.is_empty() {
2455 return Err(
2456 self.err("CREATE TRIGGER: `UPDATE OF cols` may appear at most once".into())
2457 );
2458 }
2459 update_columns = cols;
2460 }
2461 }
2462 let tok = self.peek();
2464 let Token::On = tok else {
2465 return Err(self.err(alloc::format!(
2466 "expected ON after trigger events, got {tok:?}"
2467 )));
2468 };
2469 self.advance();
2470 let table = self.expect_ident_like()?;
2471 if !matches!(self.peek(), Token::For) {
2475 return Err(self.err(alloc::format!(
2476 "expected FOR EACH ROW / STATEMENT, got {:?}",
2477 self.peek()
2478 )));
2479 }
2480 self.advance();
2481 let for_each = {
2482 let e = self.expect_ident_like()?;
2483 if !e.eq_ignore_ascii_case("each") {
2484 return Err(self.err(alloc::format!("expected EACH after FOR, got {e:?}")));
2485 }
2486 let unit = self.expect_ident_like()?;
2487 if unit.eq_ignore_ascii_case("row") {
2488 TriggerForEach::Row
2489 } else if unit.eq_ignore_ascii_case("statement") {
2490 TriggerForEach::Statement
2491 } else {
2492 return Err(self.err(alloc::format!(
2493 "expected ROW / STATEMENT after FOR EACH, got {unit:?}"
2494 )));
2495 }
2496 };
2497 let exec = self.expect_ident_like()?;
2499 if !exec.eq_ignore_ascii_case("execute") {
2500 return Err(self.err(alloc::format!(
2501 "expected EXECUTE FUNCTION/PROCEDURE in CREATE TRIGGER, got {exec:?}"
2502 )));
2503 }
2504 let fn_or_proc = self.expect_ident_like()?;
2505 if !(fn_or_proc.eq_ignore_ascii_case("function")
2506 || fn_or_proc.eq_ignore_ascii_case("procedure"))
2507 {
2508 return Err(self.err(alloc::format!(
2509 "expected FUNCTION / PROCEDURE after EXECUTE, got {fn_or_proc:?}"
2510 )));
2511 }
2512 let function = self.expect_ident_like()?;
2513 if matches!(self.peek(), Token::LParen) {
2515 self.advance();
2516 if !matches!(self.peek(), Token::RParen) {
2517 return Err(self.err(alloc::format!(
2518 "v7.12.4 trigger function calls take no args; got {:?}",
2519 self.peek()
2520 )));
2521 }
2522 self.advance();
2523 }
2524 Ok(Statement::CreateTrigger(CreateTriggerStatement {
2525 name,
2526 or_replace,
2527 timing,
2528 events,
2529 table,
2530 for_each,
2531 function,
2532 update_columns,
2533 }))
2534 }
2535
2536 fn parse_trigger_event_with_optional_of(
2540 &mut self,
2541 ) -> Result<(TriggerEvent, Vec<String>), ParseError> {
2542 let ev = self.parse_trigger_event()?;
2543 if !matches!(ev, TriggerEvent::Update) {
2544 return Ok((ev, Vec::new()));
2545 }
2546 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("of")) {
2548 return Ok((ev, Vec::new()));
2549 }
2550 self.advance(); let mut cols: Vec<String> = Vec::new();
2552 loop {
2553 cols.push(self.expect_ident_like()?);
2554 if matches!(self.peek(), Token::Comma) {
2555 self.advance();
2556 continue;
2557 }
2558 break;
2559 }
2560 if cols.is_empty() {
2561 return Err(
2562 self.err("CREATE TRIGGER: `UPDATE OF` requires at least one column name".into())
2563 );
2564 }
2565 Ok((ev, cols))
2566 }
2567
2568 pub(crate) fn parse_plpgsql_block(&mut self) -> Result<PlPgSqlBlock, ParseError> {
2575 let declarations = if matches!(
2577 self.peek(),
2578 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("declare")
2579 ) {
2580 self.advance();
2581 self.parse_plpgsql_declare_block()?
2582 } else {
2583 Vec::new()
2584 };
2585 if !matches!(self.peek(), Token::Begin) {
2590 return Err(self.err(alloc::format!(
2591 "expected BEGIN at start of plpgsql block, got {:?}",
2592 self.peek()
2593 )));
2594 }
2595 self.advance();
2596 let statements = self.parse_plpgsql_stmt_list_until_end()?;
2597 Ok(PlPgSqlBlock {
2598 declarations,
2599 statements,
2600 })
2601 }
2602
2603 fn parse_plpgsql_declare_block(&mut self) -> Result<Vec<PlPgSqlDeclare>, ParseError> {
2607 let mut out: Vec<PlPgSqlDeclare> = Vec::new();
2608 loop {
2609 if matches!(self.peek(), Token::Begin) {
2610 return Ok(out);
2611 }
2612 let name = self.expect_ident_like()?;
2613 let ty_token = self.expect_ident_like()?;
2614 let ty = match map_type_ident_to_column_type_name(&ty_token) {
2615 Some(t) => FunctionArgType::Typed(t),
2616 None => FunctionArgType::Raw(ty_token),
2617 };
2618 let default = match self.peek() {
2619 Token::ColonEq => {
2620 self.advance();
2621 Some(self.parse_expr(0)?)
2622 }
2623 Token::Eq => {
2624 self.advance();
2628 Some(self.parse_expr(0)?)
2629 }
2630 _ => None,
2631 };
2632 if !matches!(self.peek(), Token::Semicolon) {
2634 return Err(self.err(alloc::format!(
2635 "expected ; after DECLARE entry for {name:?}, got {:?}",
2636 self.peek()
2637 )));
2638 }
2639 self.advance();
2640 out.push(PlPgSqlDeclare { name, ty, default });
2641 }
2642 }
2643
2644 fn parse_plpgsql_stmt_list_until_end(&mut self) -> Result<Vec<PlPgSqlStmt>, ParseError> {
2649 let mut statements: Vec<PlPgSqlStmt> = Vec::new();
2650 loop {
2651 while matches!(self.peek(), Token::Semicolon) {
2653 self.advance();
2654 }
2655 if matches!(
2657 self.peek(),
2658 Token::Ident(s) | Token::QuotedIdent(s)
2659 if s.eq_ignore_ascii_case("end")
2660 || s.eq_ignore_ascii_case("else")
2661 || s.eq_ignore_ascii_case("elsif")
2662 || s.eq_ignore_ascii_case("elseif")
2663 ) {
2664 return Ok(statements);
2665 }
2666 let stmt = self.parse_plpgsql_stmt()?;
2669 statements.push(stmt);
2670 match self.peek() {
2671 Token::Semicolon => {
2672 self.advance();
2673 }
2674 Token::Ident(s) | Token::QuotedIdent(s)
2675 if s.eq_ignore_ascii_case("end")
2676 || s.eq_ignore_ascii_case("else")
2677 || s.eq_ignore_ascii_case("elsif")
2678 || s.eq_ignore_ascii_case("elseif") =>
2679 {
2680 }
2682 other => {
2683 return Err(self.err(alloc::format!(
2684 "expected ; or END/ELSE/ELSIF after plpgsql statement, got {other:?}"
2685 )));
2686 }
2687 }
2688 }
2689 }
2690
2691 fn parse_plpgsql_stmt(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2692 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("return"))
2694 {
2695 self.advance();
2696 return self.parse_plpgsql_return();
2697 }
2698 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("if"))
2700 {
2701 self.advance();
2702 return self.parse_plpgsql_if();
2703 }
2704 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("raise"))
2706 {
2707 self.advance();
2708 return self.parse_plpgsql_raise();
2709 }
2710 if matches!(self.peek(), Token::Select)
2721 && let Some((select_body, var_name)) = self.try_parse_plpgsql_select_into()?
2722 {
2723 return Ok(PlPgSqlStmt::SelectInto {
2724 var: var_name,
2725 body: Box::new(select_body),
2726 });
2727 }
2728 if matches!(self.peek(), Token::Insert)
2738 || matches!(self.peek(), Token::Select)
2739 || matches!(self.peek(), Token::Create)
2740 || matches!(self.peek(), Token::Drop)
2741 || matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
2742 if s.eq_ignore_ascii_case("update")
2743 || s.eq_ignore_ascii_case("delete")
2744 || s.eq_ignore_ascii_case("alter"))
2745 {
2746 let stmt = self.parse_one_statement()?;
2747 return Ok(PlPgSqlStmt::EmbeddedSql(Box::new(stmt)));
2748 }
2749 let target = self.parse_plpgsql_assign_target()?;
2752 match self.peek() {
2755 Token::ColonEq => {
2756 self.advance();
2757 }
2758 Token::Colon => {
2759 self.advance();
2760 if !matches!(self.peek(), Token::Eq) {
2761 return Err(self.err(alloc::format!(
2762 "expected := after plpgsql assign target, got `:` then {:?}",
2763 self.peek()
2764 )));
2765 }
2766 self.advance();
2767 }
2768 other => {
2769 return Err(self.err(alloc::format!(
2770 "expected := after plpgsql assign target, got {other:?}"
2771 )));
2772 }
2773 }
2774 let value = self.parse_expr(0)?;
2775 Ok(PlPgSqlStmt::Assign { target, value })
2776 }
2777
2778 fn parse_plpgsql_if(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2781 let mut branches: Vec<(Expr, Vec<PlPgSqlStmt>)> = Vec::new();
2782 let mut else_branch: Vec<PlPgSqlStmt> = Vec::new();
2783 loop {
2784 let cond = self.parse_expr(0)?;
2786 let then_kw = self.expect_ident_like()?;
2787 if !then_kw.eq_ignore_ascii_case("then") {
2788 return Err(self.err(alloc::format!(
2789 "expected THEN after IF/ELSIF condition, got {then_kw:?}"
2790 )));
2791 }
2792 let body = self.parse_plpgsql_stmt_list_until_end()?;
2793 branches.push((cond, body));
2794 match self.peek() {
2796 Token::Ident(s) | Token::QuotedIdent(s)
2797 if s.eq_ignore_ascii_case("elsif") || s.eq_ignore_ascii_case("elseif") =>
2798 {
2799 self.advance();
2800 continue;
2801 }
2802 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("else") => {
2803 self.advance();
2804 else_branch = self.parse_plpgsql_stmt_list_until_end()?;
2805 break;
2806 }
2807 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("end") => {
2808 break;
2809 }
2810 other => {
2811 return Err(self.err(alloc::format!(
2812 "expected ELSIF / ELSE / END after IF branch body, got {other:?}"
2813 )));
2814 }
2815 }
2816 }
2817 let end_kw = self.expect_ident_like()?;
2820 if !end_kw.eq_ignore_ascii_case("end") {
2821 return Err(self.err(alloc::format!("expected END IF, got {end_kw:?}")));
2822 }
2823 let if_kw = self.expect_ident_like()?;
2824 if !if_kw.eq_ignore_ascii_case("if") {
2825 return Err(self.err(alloc::format!("expected END IF, got END {if_kw:?}")));
2826 }
2827 Ok(PlPgSqlStmt::If {
2828 branches,
2829 else_branch,
2830 })
2831 }
2832
2833 fn parse_plpgsql_raise(&mut self) -> Result<PlPgSqlStmt, ParseError> {
2837 let lvl_ident = self.expect_ident_like()?;
2838 let level = match lvl_ident.to_ascii_lowercase().as_str() {
2839 "notice" => RaiseLevel::Notice,
2840 "warning" => RaiseLevel::Warning,
2841 "info" => RaiseLevel::Info,
2842 "log" => RaiseLevel::Log,
2843 "debug" => RaiseLevel::Debug,
2844 "exception" => RaiseLevel::Exception,
2845 other => {
2846 return Err(self.err(alloc::format!(
2847 "expected RAISE level (NOTICE/WARNING/INFO/LOG/DEBUG/EXCEPTION), got {other:?}"
2848 )));
2849 }
2850 };
2851 let Token::String(msg) = self.peek() else {
2855 return Err(self.err(alloc::format!(
2856 "expected RAISE message string, got {:?}",
2857 self.peek()
2858 )));
2859 };
2860 let message = msg.clone();
2861 self.advance();
2862 let mut args: Vec<Expr> = Vec::new();
2864 while matches!(self.peek(), Token::Comma) {
2865 self.advance();
2866 args.push(self.parse_expr(0)?);
2867 }
2868 Ok(PlPgSqlStmt::Raise {
2869 level,
2870 message,
2871 args,
2872 })
2873 }
2874
2875 #[allow(clippy::too_many_lines)]
2883 fn try_parse_plpgsql_select_into(
2884 &mut self,
2885 ) -> Result<Option<(SelectStatement, String)>, ParseError> {
2886 let start = self.pos;
2891 let mut into_pos: Option<usize> = None;
2892 let mut depth: i32 = 0;
2893 let mut i = start + 1;
2894 while i < self.tokens.len() {
2895 match &self.tokens[i] {
2896 Token::LParen => depth += 1,
2897 Token::RParen => depth -= 1,
2898 Token::Semicolon if depth == 0 => break,
2899 Token::Ident(s)
2900 if depth == 0
2901 && (s.eq_ignore_ascii_case("end")
2902 || s.eq_ignore_ascii_case("else")
2903 || s.eq_ignore_ascii_case("elsif")) =>
2904 {
2905 break;
2906 }
2907 Token::Into if depth == 0 => {
2908 into_pos = Some(i);
2909 break;
2910 }
2911 _ => {}
2912 }
2913 i += 1;
2914 }
2915 let Some(into_at) = into_pos else {
2916 return Ok(None);
2917 };
2918 let var = match self.tokens.get(into_at + 1) {
2922 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
2923 other => {
2924 return Err(self.err(alloc::format!(
2925 "expected variable name after SELECT … INTO, got {other:?}"
2926 )));
2927 }
2928 };
2929 let mut end = into_at + 2;
2932 let mut depth2: i32 = 0;
2933 while end < self.tokens.len() {
2934 match &self.tokens[end] {
2935 Token::LParen => depth2 += 1,
2936 Token::RParen => depth2 -= 1,
2937 Token::Semicolon if depth2 == 0 => break,
2938 Token::Ident(s)
2939 if depth2 == 0
2940 && (s.eq_ignore_ascii_case("end")
2941 || s.eq_ignore_ascii_case("else")
2942 || s.eq_ignore_ascii_case("elsif")) =>
2943 {
2944 break;
2945 }
2946 _ => {}
2947 }
2948 end += 1;
2949 }
2950 let mut rebuilt: Vec<Token> = Vec::with_capacity(end - start);
2955 for j in start..into_at {
2956 rebuilt.push(self.tokens[j].clone());
2957 }
2958 for j in (into_at + 2)..end {
2959 rebuilt.push(self.tokens[j].clone());
2960 }
2961 rebuilt.push(Token::Eof);
2962 let saved_pos = self.pos;
2963 let saved_tokens = core::mem::replace(&mut self.tokens, rebuilt);
2964 self.pos = 0;
2965 if !matches!(self.peek(), Token::Select) {
2967 self.tokens = saved_tokens;
2968 self.pos = saved_pos;
2969 return Err(self.err("plpgsql SELECT … INTO: rebuilt stream missing SELECT".into()));
2970 }
2971 let sel = self.parse_select_stmt();
2972 self.tokens = saved_tokens;
2973 self.pos = end;
2974 let sel = sel?;
2975 let Statement::Select(body) = sel else {
2976 return Err(self.err(alloc::format!(
2977 "plpgsql SELECT … INTO: rebuilt SELECT did not produce a Select node, got {sel:?}"
2978 )));
2979 };
2980 Ok(Some((body, var)))
2981 }
2982
2983 fn parse_plpgsql_assign_target(&mut self) -> Result<AssignTarget, ParseError> {
2984 let head = match self.advance() {
2998 Token::Ident(s) | Token::QuotedIdent(s) => s,
2999 other => {
3000 return Err(self.err(alloc::format!(
3001 "expected NEW / OLD / <local_var> as plpgsql assign target, got {other:?}"
3002 )));
3003 }
3004 };
3005 if matches!(self.peek(), Token::Dot) {
3006 self.advance();
3007 let col = self.expect_ident_like()?;
3008 if head.eq_ignore_ascii_case("new") {
3009 return Ok(AssignTarget::NewColumn(col));
3010 }
3011 if head.eq_ignore_ascii_case("old") {
3012 return Ok(AssignTarget::OldColumn(col));
3013 }
3014 return Err(self.err(alloc::format!(
3015 "plpgsql assign target must be NEW.<col> / OLD.<col> / <local_var>; \
3016 got {head:?}.<col>"
3017 )));
3018 }
3019 Ok(AssignTarget::Local(head))
3020 }
3021
3022 fn parse_plpgsql_return(&mut self) -> Result<PlPgSqlStmt, ParseError> {
3023 match self.peek() {
3025 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("new") => {
3026 self.advance();
3027 return Ok(PlPgSqlStmt::Return(ReturnTarget::New));
3028 }
3029 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("old") => {
3030 self.advance();
3031 return Ok(PlPgSqlStmt::Return(ReturnTarget::Old));
3032 }
3033 Token::Null => {
3034 self.advance();
3035 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3036 }
3037 Token::Semicolon => {
3040 return Ok(PlPgSqlStmt::Return(ReturnTarget::Null));
3041 }
3042 _ => {}
3043 }
3044 let e = self.parse_expr(0)?;
3046 Ok(PlPgSqlStmt::Return(ReturnTarget::Expr(e)))
3047 }
3048
3049 fn parse_trigger_event(&mut self) -> Result<TriggerEvent, ParseError> {
3050 if matches!(self.peek(), Token::Insert) {
3055 self.advance();
3056 return Ok(TriggerEvent::Insert);
3057 }
3058 match self.peek() {
3059 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3060 self.advance();
3061 Ok(TriggerEvent::Update)
3062 }
3063 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3064 self.advance();
3065 Ok(TriggerEvent::Delete)
3066 }
3067 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("truncate") => {
3068 self.advance();
3069 Ok(TriggerEvent::Truncate)
3070 }
3071 other => Err(self.err(alloc::format!(
3072 "expected INSERT / UPDATE / DELETE / TRUNCATE in trigger event list, got {other:?}"
3073 ))),
3074 }
3075 }
3076
3077 fn parse_create_publication_after_keyword(&mut self) -> Result<Statement, ParseError> {
3084 let name = self.expect_ident_or_string()?;
3085 let scope = if matches!(self.peek(), Token::For) {
3088 self.advance();
3089 if matches!(self.peek(), Token::All) {
3090 self.advance();
3091 if !matches!(self.peek(), Token::Tables) {
3092 return Err(self.err(format!(
3093 "expected TABLES after FOR ALL, got {:?}",
3094 self.peek()
3095 )));
3096 }
3097 self.advance();
3098 if matches!(self.peek(), Token::Except) {
3099 self.advance();
3100 let tables = self.parse_publication_table_list()?;
3101 PublicationScope::AllTablesExcept(tables)
3102 } else {
3103 PublicationScope::AllTables
3104 }
3105 } else if matches!(self.peek(), Token::Table | Token::Tables) {
3106 self.advance();
3109 let tables = self.parse_publication_table_list()?;
3110 PublicationScope::ForTables(tables)
3111 } else {
3112 return Err(self.err(format!(
3113 "expected ALL TABLES or TABLE <list> after FOR, got {:?}",
3114 self.peek()
3115 )));
3116 }
3117 } else {
3118 PublicationScope::AllTables
3119 };
3120 Ok(Statement::CreatePublication(CreatePublicationStatement {
3121 name,
3122 scope,
3123 }))
3124 }
3125
3126 fn parse_publication_table_list(&mut self) -> Result<Vec<String>, ParseError> {
3131 let first = self.expect_ident_like()?;
3132 let mut out = alloc::vec![first];
3133 while matches!(self.peek(), Token::Comma) {
3134 self.advance();
3135 out.push(self.expect_ident_like()?);
3136 }
3137 Ok(out)
3138 }
3139
3140 fn parse_create_subscription_after_keyword(&mut self) -> Result<Statement, ParseError> {
3148 let name = self.expect_ident_or_string()?;
3149 if !matches!(self.peek(), Token::Connection) {
3150 return Err(self.err(format!(
3151 "expected CONNECTION after CREATE SUBSCRIPTION <name>, got {:?}",
3152 self.peek()
3153 )));
3154 }
3155 self.advance();
3156 let conn_str = self.expect_string_literal()?;
3157 if !matches!(self.peek(), Token::Publication) {
3158 return Err(self.err(format!(
3159 "expected PUBLICATION after CONNECTION '<conn>', got {:?}",
3160 self.peek()
3161 )));
3162 }
3163 self.advance();
3164 let first = self.expect_ident_like()?;
3167 let mut publications = alloc::vec![first];
3168 while matches!(self.peek(), Token::Comma) {
3169 self.advance();
3170 publications.push(self.expect_ident_like()?);
3171 }
3172 Ok(Statement::CreateSubscription(CreateSubscriptionStatement {
3173 name,
3174 conn_str,
3175 publications,
3176 }))
3177 }
3178
3179 fn parse_set_param_name(&mut self) -> Result<String, ParseError> {
3186 let mut name = self.expect_ident_like()?;
3187 while matches!(self.peek(), Token::Dot) {
3188 self.advance();
3189 let next = self.expect_ident_like()?;
3190 name.push('.');
3191 name.push_str(&next);
3192 }
3193 Ok(name.to_ascii_lowercase())
3194 }
3195
3196 fn parse_set_value(&mut self) -> Result<crate::ast::SetValue, ParseError> {
3197 match self.advance() {
3198 Token::String(s) => Ok(crate::ast::SetValue::String(s)),
3199 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("default") => {
3200 Ok(crate::ast::SetValue::Default)
3201 }
3202 Token::Ident(s) | Token::QuotedIdent(s) => {
3203 let mut accum = s;
3204 while matches!(self.peek(), Token::Dot) {
3205 self.advance();
3206 let next = self.expect_ident_like()?;
3207 accum.push('.');
3208 accum.push_str(&next);
3209 }
3210 Ok(crate::ast::SetValue::Ident(accum))
3211 }
3212 Token::Integer(n) => Ok(crate::ast::SetValue::Number(n.to_string())),
3213 Token::Float(f) => Ok(crate::ast::SetValue::Number(f.to_string())),
3214 Token::On => Ok(crate::ast::SetValue::Ident("on".to_string())),
3219 Token::True => Ok(crate::ast::SetValue::Ident("true".to_string())),
3220 Token::False => Ok(crate::ast::SetValue::Ident("false".to_string())),
3221 Token::SessionVar(s) => Ok(crate::ast::SetValue::Ident(s)),
3227 Token::Minus => match self.advance() {
3232 Token::Integer(n) => Ok(crate::ast::SetValue::Number(alloc::format!("-{n}"))),
3233 Token::Float(f) => Ok(crate::ast::SetValue::Number(alloc::format!("-{f}"))),
3234 other => Err(self.err(format!(
3235 "expected numeric after `-` in SET value, got {other:?}"
3236 ))),
3237 },
3238 other => Err(self.err(format!(
3239 "expected literal, identifier, or DEFAULT after `=` in SET, got {other:?}"
3240 ))),
3241 }
3242 }
3243
3244 fn parse_wait_after_keyword(&mut self) -> Result<Statement, ParseError> {
3245 if !matches!(self.peek(), Token::For) {
3249 return Err(self.err(format!("expected FOR after WAIT, got {:?}", self.peek())));
3250 }
3251 self.advance();
3252 self.expect_keyword_ident("wal")?;
3253 self.expect_keyword_ident("position")?;
3254 let pos = self.expect_u64_literal()?;
3255 let timeout_ms = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("with"))
3256 {
3257 self.advance();
3258 self.expect_keyword_ident("timeout")?;
3259 Some(self.expect_u64_literal()?)
3260 } else {
3261 None
3262 };
3263 Ok(Statement::WaitForWalPosition { pos, timeout_ms })
3264 }
3265
3266 fn expect_u64_literal(&mut self) -> Result<u64, ParseError> {
3270 match self.advance() {
3271 Token::Integer(n) if n >= 0 => Ok(n as u64),
3272 Token::Integer(n) => Err(ParseError {
3273 message: format!("expected non-negative integer, got {n}"),
3274 token_pos: self.pos.saturating_sub(1),
3275 }),
3276 other => Err(ParseError {
3277 message: format!("expected integer literal, got {other:?}"),
3278 token_pos: self.pos.saturating_sub(1),
3279 }),
3280 }
3281 }
3282
3283 fn parse_create_user_after_keyword(&mut self) -> Result<Statement, ParseError> {
3287 let name = self.expect_ident_or_string()?;
3288 self.expect_keyword_ident("with")?;
3289 self.expect_keyword_ident("password")?;
3290 let password = self.expect_string_literal()?;
3291 let role = if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
3292 && s.eq_ignore_ascii_case("role")
3293 {
3294 self.advance();
3295 self.expect_string_literal()?
3296 } else {
3297 "readonly".to_string()
3298 };
3299 Ok(Statement::CreateUser(crate::ast::CreateUserStatement {
3300 name,
3301 password,
3302 role,
3303 }))
3304 }
3305
3306 fn parse_update_after_keyword(&mut self) -> Result<Statement, ParseError> {
3309 let table = self.expect_ident_like()?;
3310 self.expect_keyword_ident("set")?;
3311 let mut assignments = Vec::new();
3312 loop {
3313 let col = self.expect_ident_like()?;
3314 if !matches!(self.peek(), Token::Eq) {
3315 return Err(self.err(format!(
3316 "expected `=` after column name in UPDATE SET, got {:?}",
3317 self.peek()
3318 )));
3319 }
3320 self.advance();
3321 let value = self.parse_expr(0)?;
3322 assignments.push((col, value));
3323 if matches!(self.peek(), Token::Comma) {
3324 self.advance();
3325 continue;
3326 }
3327 break;
3328 }
3329 let where_ = if matches!(self.peek(), Token::Where) {
3330 self.advance();
3331 Some(self.parse_expr(0)?)
3332 } else {
3333 None
3334 };
3335 let returning = self.parse_optional_returning()?;
3336 Ok(Statement::Update(crate::ast::UpdateStatement {
3337 table,
3338 assignments,
3339 where_,
3340 returning,
3341 }))
3342 }
3343
3344 fn parse_delete_after_keyword(&mut self) -> Result<Statement, ParseError> {
3347 if !matches!(self.peek(), Token::From) {
3348 return Err(self.err(format!("expected FROM after DELETE, got {:?}", self.peek())));
3349 }
3350 self.advance();
3351 let table = self.expect_ident_like()?;
3352 let where_ = if matches!(self.peek(), Token::Where) {
3353 self.advance();
3354 Some(self.parse_expr(0)?)
3355 } else {
3356 None
3357 };
3358 let returning = self.parse_optional_returning()?;
3359 Ok(Statement::Delete(crate::ast::DeleteStatement {
3360 table,
3361 where_,
3362 returning,
3363 }))
3364 }
3365
3366 fn parse_merge_after_keyword(&mut self) -> Result<Statement, ParseError> {
3376 let is_into_kw = matches!(self.peek(), Token::Into)
3378 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("into"));
3379 if !is_into_kw {
3380 return Err(self.err(format!("expected INTO after MERGE, got {:?}", self.peek())));
3381 }
3382 self.advance();
3383 let target = self.expect_ident_like()?;
3384 let target_alias = match self.peek() {
3386 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("using") => {
3387 Some(self.expect_ident_like()?)
3388 }
3389 _ => None,
3390 };
3391 let is_using_kw = matches!(
3393 self.peek(),
3394 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("using")
3395 );
3396 if !is_using_kw {
3397 return Err(self.err(format!(
3398 "expected USING after MERGE INTO target, got {:?}",
3399 self.peek()
3400 )));
3401 }
3402 self.advance();
3403 let source = self.expect_ident_like()?;
3404 let source_alias = match self.peek() {
3405 Token::Ident(s) | Token::QuotedIdent(s) if !s.eq_ignore_ascii_case("on") => {
3406 Some(self.expect_ident_like()?)
3407 }
3408 _ => None,
3409 };
3410 if !matches!(self.peek(), Token::On) {
3412 return Err(self.err(format!(
3413 "expected ON after MERGE … USING source, got {:?}",
3414 self.peek()
3415 )));
3416 }
3417 self.advance();
3418 let on = self.parse_expr(0)?;
3419 let mut clauses: Vec<crate::ast::MergeWhenClause> = Vec::new();
3421 loop {
3422 let is_when_kw = matches!(
3423 self.peek(),
3424 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("when")
3425 );
3426 if !is_when_kw {
3427 break;
3428 }
3429 self.advance(); let matched = if matches!(self.peek(), Token::Not) {
3432 self.advance();
3433 crate::ast::MergeMatched::NotMatched
3434 } else {
3435 crate::ast::MergeMatched::Matched
3436 };
3437 let is_matched_kw = matches!(
3438 self.peek(),
3439 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("matched")
3440 );
3441 if !is_matched_kw {
3442 return Err(self.err(format!(
3443 "expected MATCHED in WHEN clause, got {:?}",
3444 self.peek()
3445 )));
3446 }
3447 self.advance();
3448 let condition = if matches!(self.peek(), Token::And) {
3450 self.advance();
3451 Some(self.parse_expr(0)?)
3452 } else {
3453 None
3454 };
3455 let is_then_kw = matches!(
3457 self.peek(),
3458 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("then")
3459 );
3460 if !is_then_kw {
3461 return Err(self.err(format!(
3462 "expected THEN in WHEN clause, got {:?}",
3463 self.peek()
3464 )));
3465 }
3466 self.advance();
3467 let action = match self.peek().clone() {
3469 Token::Insert => {
3470 self.advance();
3471 if !matches!(self.peek(), Token::LParen) {
3473 return Err(self.err(format!(
3474 "expected '(' after INSERT in MERGE, got {:?}",
3475 self.peek()
3476 )));
3477 }
3478 self.advance();
3479 let mut columns: Vec<String> = Vec::new();
3480 loop {
3481 columns.push(self.expect_ident_like()?);
3482 if matches!(self.peek(), Token::Comma) {
3483 self.advance();
3484 continue;
3485 }
3486 break;
3487 }
3488 if !matches!(self.peek(), Token::RParen) {
3489 return Err(self.err(format!(
3490 "expected ')' after INSERT column list, got {:?}",
3491 self.peek()
3492 )));
3493 }
3494 self.advance();
3495 if !matches!(self.peek(), Token::Values) {
3497 return Err(self.err(format!(
3498 "expected VALUES in MERGE INSERT, got {:?}",
3499 self.peek()
3500 )));
3501 }
3502 self.advance();
3503 if !matches!(self.peek(), Token::LParen) {
3504 return Err(self.err(format!(
3505 "expected '(' after VALUES in MERGE INSERT, got {:?}",
3506 self.peek()
3507 )));
3508 }
3509 self.advance();
3510 let mut values: Vec<crate::ast::Expr> = Vec::new();
3511 loop {
3512 values.push(self.parse_expr(0)?);
3513 if matches!(self.peek(), Token::Comma) {
3514 self.advance();
3515 continue;
3516 }
3517 break;
3518 }
3519 if !matches!(self.peek(), Token::RParen) {
3520 return Err(self.err(format!(
3521 "expected ')' after MERGE INSERT values, got {:?}",
3522 self.peek()
3523 )));
3524 }
3525 self.advance();
3526 if columns.len() != values.len() {
3527 return Err(self.err(format!(
3528 "MERGE INSERT column count ({}) ≠ value count ({})",
3529 columns.len(),
3530 values.len()
3531 )));
3532 }
3533 crate::ast::MergeAction::Insert { columns, values }
3534 }
3535 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
3536 self.advance();
3537 let is_set_kw = matches!(
3539 self.peek(),
3540 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set")
3541 );
3542 if !is_set_kw {
3543 return Err(self.err(format!(
3544 "expected SET after UPDATE in MERGE, got {:?}",
3545 self.peek()
3546 )));
3547 }
3548 self.advance();
3549 let mut assignments: Vec<(String, crate::ast::Expr)> = Vec::new();
3550 loop {
3551 let col = self.expect_ident_like()?;
3552 if !matches!(self.peek(), Token::Eq) {
3553 return Err(self.err(format!(
3554 "expected '=' in MERGE UPDATE assignment, got {:?}",
3555 self.peek()
3556 )));
3557 }
3558 self.advance();
3559 let expr = self.parse_expr(0)?;
3560 assignments.push((col, expr));
3561 if matches!(self.peek(), Token::Comma) {
3562 self.advance();
3563 continue;
3564 }
3565 break;
3566 }
3567 crate::ast::MergeAction::Update { assignments }
3568 }
3569 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("delete") => {
3570 self.advance();
3571 crate::ast::MergeAction::Delete
3572 }
3573 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {
3574 self.advance();
3575 let is_nothing_kw = matches!(
3576 self.peek(),
3577 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing")
3578 );
3579 if !is_nothing_kw {
3580 return Err(self.err(format!(
3581 "expected NOTHING after DO in MERGE clause, got {:?}",
3582 self.peek()
3583 )));
3584 }
3585 self.advance();
3586 crate::ast::MergeAction::DoNothing
3587 }
3588 other => {
3589 return Err(self.err(format!(
3590 "expected INSERT / UPDATE / DELETE / DO NOTHING in MERGE clause, got {other:?}"
3591 )));
3592 }
3593 };
3594 clauses.push(crate::ast::MergeWhenClause {
3595 matched,
3596 condition,
3597 action,
3598 });
3599 }
3600 if clauses.is_empty() {
3601 return Err(self.err(String::from("MERGE requires at least one WHEN clause")));
3602 }
3603 Ok(Statement::Merge(crate::ast::MergeStatement {
3604 target,
3605 target_alias,
3606 source,
3607 source_alias,
3608 on,
3609 clauses,
3610 }))
3611 }
3612
3613 fn parse_optional_returning(
3618 &mut self,
3619 ) -> Result<Option<Vec<crate::ast::SelectItem>>, ParseError> {
3620 let is_returning_kw = matches!(
3621 self.peek(),
3622 Token::Ident(s) if s.eq_ignore_ascii_case("returning")
3623 );
3624 if !is_returning_kw {
3625 return Ok(None);
3626 }
3627 self.advance();
3628 let mut items = Vec::new();
3629 loop {
3630 items.push(self.parse_select_item()?);
3631 if matches!(self.peek(), Token::Comma) {
3632 self.advance();
3633 continue;
3634 }
3635 break;
3636 }
3637 Ok(Some(items))
3638 }
3639
3640 fn parse_alter_after_keyword(&mut self) -> Result<Statement, ParseError> {
3648 match self.advance() {
3655 Token::Index => {}
3656 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("index") => {}
3657 Token::Table => {
3660 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3661 self.advance();
3662 }
3663 return self.parse_alter_table_after_keyword();
3664 }
3665 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("table") => {
3666 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("only")) {
3667 self.advance();
3668 }
3669 return self.parse_alter_table_after_keyword();
3670 }
3671 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("sequence") => {
3674 return self.parse_alter_sequence_after_keyword();
3675 }
3676 Token::Ident(s) | Token::QuotedIdent(s)
3682 if matches!(
3683 s.to_ascii_lowercase().as_str(),
3684 "view"
3685 | "function"
3686 | "type"
3687 | "domain"
3688 | "database"
3689 | "role"
3690 | "schema"
3691 | "owner"
3692 | "default"
3693 | "extension"
3694 | "materialized"
3695 | "policy"
3696 | "publication"
3697 | "subscription"
3698 ) =>
3699 {
3700 self.consume_until_statement_boundary();
3701 return Ok(Statement::Empty);
3702 }
3703 other => {
3704 return Err(self.err(format!(
3705 "expected INDEX / TABLE / SEQUENCE / VIEW / FUNCTION / TYPE / OWNER / etc \
3706 after ALTER, got {other:?}"
3707 )));
3708 }
3709 }
3710 let if_exists = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3715 let next = self.tokens.get(self.pos + 1);
3716 if matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
3717 self.advance();
3718 self.advance();
3719 true
3720 } else {
3721 false
3722 }
3723 } else {
3724 false
3725 };
3726 let name = self.expect_ident_like()?;
3727 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("rename")) {
3731 self.advance();
3732 if matches!(self.peek(), Token::To) {
3733 self.advance();
3734 } else {
3735 self.expect_keyword_ident("to")?;
3736 }
3737 let new = self.expect_ident_like()?;
3738 return Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3739 name,
3740 target: crate::ast::AlterIndexTarget::Rename { new, if_exists },
3741 }));
3742 }
3743 self.expect_keyword_ident("rebuild")?;
3745 let encoding = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
3747 self.advance();
3748 if !matches!(self.peek(), Token::LParen) {
3749 return Err(self.err(format!(
3750 "expected '(' after WITH in ALTER INDEX REBUILD, got {:?}",
3751 self.peek()
3752 )));
3753 }
3754 self.advance();
3755 self.expect_keyword_ident("encoding")?;
3756 if !matches!(self.peek(), Token::Eq) {
3757 return Err(self.err(format!(
3758 "expected '=' after encoding in ALTER INDEX REBUILD, got {:?}",
3759 self.peek()
3760 )));
3761 }
3762 self.advance();
3763 let enc_ident = match self.advance() {
3764 Token::Ident(s) | Token::QuotedIdent(s) => s,
3765 other => {
3766 return Err(self.err(format!("expected encoding name after =, got {other:?}")));
3767 }
3768 };
3769 let enc = match enc_ident.to_ascii_lowercase().as_str() {
3770 "f32" => VecEncoding::F32,
3771 "sq8" => VecEncoding::Sq8,
3772 "half" => VecEncoding::F16,
3773 other => {
3774 return Err(self.err(format!(
3775 "unknown vector encoding {other:?} in ALTER INDEX REBUILD; supported: F32, SQ8, HALF"
3776 )));
3777 }
3778 };
3779 if !matches!(self.peek(), Token::RParen) {
3780 return Err(self.err(format!(
3781 "expected ')' after encoding value, got {:?}",
3782 self.peek()
3783 )));
3784 }
3785 self.advance();
3786 Some(enc)
3787 } else {
3788 None
3789 };
3790 Ok(Statement::AlterIndex(crate::ast::AlterIndexStatement {
3791 name,
3792 target: crate::ast::AlterIndexTarget::Rebuild { encoding },
3793 }))
3794 }
3795
3796 fn parse_alter_table_after_keyword(&mut self) -> Result<Statement, ParseError> {
3802 let table_name = self.expect_ident_like()?;
3803 let mut targets: Vec<crate::ast::AlterTableTarget> = Vec::new();
3804 loop {
3805 let subaction = self.parse_alter_table_subaction()?;
3806 targets.extend(subaction);
3810 if matches!(self.peek(), Token::Comma) {
3811 self.advance();
3812 continue;
3813 }
3814 break;
3815 }
3816 Ok(Statement::AlterTable(crate::ast::AlterTableStatement {
3817 name: table_name,
3818 targets,
3819 }))
3820 }
3821
3822 fn parse_alter_table_subaction(
3826 &mut self,
3827 ) -> Result<Vec<crate::ast::AlterTableTarget>, ParseError> {
3828 match self.peek() {
3829 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
3830 self.advance();
3831 let setting = self.expect_ident_like()?;
3832 if !setting.eq_ignore_ascii_case("hot_tier_bytes") {
3833 return Err(self.err(alloc::format!(
3834 "ALTER TABLE SET: unknown setting {setting:?}; supported: hot_tier_bytes"
3835 )));
3836 }
3837 if !matches!(self.peek(), Token::Eq) {
3838 return Err(self.err(alloc::format!(
3839 "expected '=' after hot_tier_bytes, got {:?}",
3840 self.peek()
3841 )));
3842 }
3843 self.advance();
3844 let n = self.expect_u64_literal()?;
3845 Ok(alloc::vec![crate::ast::AlterTableTarget::SetHotTierBytes(n)])
3846 }
3847 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
3848 self.advance();
3849 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint"))
3856 {
3857 let kind_pos = self.pos + 2;
3860 let kind = self.tokens.get(kind_pos).cloned();
3861 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("foreign"))
3862 {
3863 let fk = self.parse_table_level_fk()?;
3864 return Ok(alloc::vec![
3865 crate::ast::AlterTableTarget::AddForeignKey(fk)
3866 ]);
3867 }
3868 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary"))
3869 {
3870 self.advance(); let _name = self.expect_ident_like()?;
3872 self.advance(); self.expect_keyword_ident("key")?;
3874 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3875 return Ok(alloc::vec![
3876 crate::ast::AlterTableTarget::AddTableConstraint(
3877 crate::ast::TableConstraint::PrimaryKey {
3878 name: None,
3879 columns: cols,
3880 }
3881 )
3882 ]);
3883 }
3884 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique"))
3885 {
3886 self.advance(); let _name = self.expect_ident_like()?;
3888 let uc = self.parse_table_level_unique()?;
3894 return Ok(alloc::vec![
3895 crate::ast::AlterTableTarget::AddTableConstraint(uc)
3896 ]);
3897 }
3898 if matches!(&kind, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check"))
3899 {
3900 self.advance(); let _name = self.expect_ident_like()?;
3902 self.advance(); if !matches!(self.peek(), Token::LParen) {
3904 return Err(self.err(alloc::format!(
3905 "expected '(' after CHECK, got {:?}", self.peek()
3906 )));
3907 }
3908 self.advance();
3909 let expr = self.parse_expr(0)?;
3910 if matches!(self.peek(), Token::RParen) {
3911 self.advance();
3912 }
3913 return Ok(alloc::vec![
3914 crate::ast::AlterTableTarget::AddTableConstraint(
3915 crate::ast::TableConstraint::Check { name: None, expr }
3916 )
3917 ]);
3918 }
3919 }
3922 let is_fk = matches!(
3923 self.peek(),
3924 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
3925 || s.eq_ignore_ascii_case("foreign")
3926 );
3927 if is_fk {
3928 let fk = self.parse_table_level_fk()?;
3929 return Ok(alloc::vec![crate::ast::AlterTableTarget::AddForeignKey(fk)]);
3930 }
3931 match self.peek().clone() {
3934 Token::Ident(s) if s.eq_ignore_ascii_case("primary") => {
3935 self.advance();
3936 self.expect_keyword_ident("key")?;
3937 let cols = self.parse_paren_ident_list("PRIMARY KEY")?;
3938 return Ok(alloc::vec![
3939 crate::ast::AlterTableTarget::AddTableConstraint(
3940 crate::ast::TableConstraint::PrimaryKey {
3941 name: None,
3942 columns: cols,
3943 }
3944 )
3945 ]);
3946 }
3947 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
3948 let uc = self.parse_table_level_unique()?;
3950 return Ok(alloc::vec![
3951 crate::ast::AlterTableTarget::AddTableConstraint(uc)
3952 ]);
3953 }
3954 _ => {}
3955 }
3956 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
3957 self.advance();
3958 }
3959 let mut if_not_exists = false;
3960 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
3961 self.advance();
3962 if !matches!(self.peek(), Token::Not) {
3963 return Err(self.err(alloc::format!(
3964 "expected NOT after IF in ALTER TABLE ADD COLUMN, got {:?}",
3965 self.peek()
3966 )));
3967 }
3968 self.advance();
3969 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("exists")) {
3970 return Err(self.err(alloc::format!(
3971 "expected EXISTS after IF NOT in ALTER TABLE ADD COLUMN, got {:?}",
3972 self.peek()
3973 )));
3974 }
3975 self.advance();
3976 if_not_exists = true;
3977 }
3978 let (column, col_level_fk) = self.parse_column_def_with_fk()?;
3982 let col_name = column.name.clone();
3983 let mut out = alloc::vec![crate::ast::AlterTableTarget::AddColumn {
3984 column,
3985 if_not_exists,
3986 }];
3987 if let Some(mut fk) = col_level_fk {
3988 if fk.columns.is_empty() {
3989 fk.columns.push(col_name);
3990 }
3991 out.push(crate::ast::AlterTableTarget::AddForeignKey(fk));
3992 }
3993 Ok(out)
3994 }
3995 Token::Drop => {
3996 self.advance();
3997 let subject = match self.peek() {
4004 Token::Ident(s) if s.eq_ignore_ascii_case("constraint") => {
4005 self.advance();
4006 "constraint"
4007 }
4008 Token::Ident(s) if s.eq_ignore_ascii_case("column") => {
4009 self.advance();
4010 "column"
4011 }
4012 Token::Ident(_) | Token::QuotedIdent(_) => "column",
4016 other => {
4017 return Err(self.err(alloc::format!(
4018 "expected COLUMN / CONSTRAINT after DROP in ALTER TABLE, got {other:?}"
4019 )));
4020 }
4021 };
4022 let mut if_exists = false;
4023 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if")) {
4024 let n1 = self.tokens.get(self.pos + 1);
4025 if matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")) {
4026 self.advance();
4027 self.advance();
4028 if_exists = true;
4029 }
4030 }
4031 let name = self.expect_ident_like()?;
4032 let mut cascade = false;
4033 if matches!(
4034 self.peek(),
4035 Token::Ident(s) if s.eq_ignore_ascii_case("cascade")
4036 || s.eq_ignore_ascii_case("restrict")
4037 ) {
4038 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("cascade"))
4039 {
4040 cascade = true;
4041 }
4042 self.advance();
4043 }
4044 if subject == "constraint" {
4045 Ok(alloc::vec![crate::ast::AlterTableTarget::DropForeignKey {
4046 name,
4047 if_exists,
4048 }])
4049 } else {
4050 Ok(alloc::vec![crate::ast::AlterTableTarget::DropColumn {
4051 column: name,
4052 if_exists,
4053 cascade,
4054 }])
4055 }
4056 }
4057 Token::Ident(s) if s.eq_ignore_ascii_case("alter") => {
4058 self.advance();
4059 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4060 self.advance();
4061 }
4062 let col_name = self.expect_ident_like()?;
4063 match self.peek() {
4064 Token::Ident(s) if s.eq_ignore_ascii_case("type") => {
4065 self.advance();
4066 }
4067 Token::Ident(s) if s.eq_ignore_ascii_case("set") => {
4075 let is_default_nextval =
4086 matches!(self.tokens.get(self.pos + 1), Some(Token::Default))
4087 && matches!(
4088 self.tokens.get(self.pos + 2),
4089 Some(Token::Ident(f)) if f.eq_ignore_ascii_case("nextval")
4090 );
4091 let seq_name = if is_default_nextval {
4094 self.scan_sequence_name_until_boundary()
4095 } else {
4096 self.consume_until_statement_boundary();
4097 None
4098 };
4099 if is_default_nextval {
4100 return Ok(alloc::vec![
4101 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4102 column: col_name,
4103 seq_name,
4104 }
4105 ]);
4106 }
4107 return Ok(Vec::new());
4113 }
4114 Token::Ident(s) if s.eq_ignore_ascii_case("drop") => {
4115 self.consume_until_statement_boundary();
4117 return Ok(Vec::new());
4118 }
4119 Token::Ident(s) if s.eq_ignore_ascii_case("add") => {
4120 let is_generated = matches!(
4128 self.tokens.get(self.pos + 1),
4129 Some(Token::Ident(g)) if g.eq_ignore_ascii_case("generated")
4130 );
4131 if !is_generated {
4132 return Err(self.err(alloc::format!(
4133 "expected GENERATED after ALTER COLUMN {col_name} ADD, got {:?}",
4134 self.tokens.get(self.pos + 1)
4135 )));
4136 }
4137 let seq_name = self.scan_sequence_name_until_boundary();
4138 return Ok(alloc::vec![
4139 crate::ast::AlterTableTarget::SetColumnAutoIncrement {
4140 column: col_name,
4141 seq_name,
4142 }
4143 ]);
4144 }
4145 other => {
4146 return Err(self.err(alloc::format!(
4147 "expected TYPE / SET / DROP / ADD after ALTER COLUMN <name>, got {other:?}"
4148 )));
4149 }
4150 }
4151 let new_type = self.parse_column_type_name()?;
4152 let using = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using"))
4153 {
4154 self.advance();
4155 Some(self.parse_expr(0)?)
4156 } else {
4157 None
4158 };
4159 Ok(alloc::vec![crate::ast::AlterTableTarget::AlterColumnType {
4160 column: col_name,
4161 new_type,
4162 using,
4163 }])
4164 }
4165 Token::Ident(s) if s.eq_ignore_ascii_case("rename") => {
4172 self.advance();
4173 if matches!(self.peek(), Token::To)
4178 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("to"))
4179 {
4180 self.advance();
4181 let new = self.expect_ident_like()?;
4182 return Ok(alloc::vec![crate::ast::AlterTableTarget::RenameTable {
4183 new,
4184 }]);
4185 }
4186 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("column")) {
4187 self.advance();
4188 }
4189 let old = self.expect_ident_like()?;
4190 if matches!(self.peek(), Token::To) {
4193 self.advance();
4194 } else {
4195 self.expect_keyword_ident("to")?;
4196 }
4197 let new = self.expect_ident_like()?;
4198 Ok(alloc::vec![crate::ast::AlterTableTarget::RenameColumn {
4199 old,
4200 new,
4201 }])
4202 }
4203 Token::Ident(s)
4210 if s.eq_ignore_ascii_case("enable") || s.eq_ignore_ascii_case("disable") =>
4211 {
4212 let enabled = s.eq_ignore_ascii_case("enable");
4213 self.advance();
4214 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("keys")) {
4224 self.advance();
4225 return Ok(Vec::new());
4226 }
4227 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("trigger")) {
4228 return Err(self.err(alloc::format!(
4229 "expected TRIGGER after {}, got {:?}",
4230 if enabled { "ENABLE" } else { "DISABLE" },
4231 self.peek()
4232 )));
4233 }
4234 self.advance();
4235 let which = if matches!(self.peek(), Token::All)
4238 || matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("all"))
4239 {
4240 self.advance();
4241 crate::ast::TriggerSelector::All
4242 } else {
4243 let name = self.expect_ident_like()?;
4244 crate::ast::TriggerSelector::Named(name)
4245 };
4246 Ok(alloc::vec![crate::ast::AlterTableTarget::SetTriggerEnabled {
4247 which,
4248 enabled,
4249 }])
4250 }
4251 other => Err(self.err(alloc::format!(
4252 "expected SET / ADD / DROP / ALTER / RENAME / ENABLE / DISABLE in ALTER TABLE, got {other:?}"
4253 ))),
4254 }
4255 }
4256
4257 fn try_peek_meta_qualified(&mut self) -> Option<String> {
4266 let schema = match self.tokens.get(self.pos) {
4268 Some(Token::Ident(s) | Token::QuotedIdent(s)) => s.clone(),
4269 _ => return None,
4270 };
4271 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4273 return None;
4274 }
4275 let tbl = match self.tokens.get(self.pos + 2)? {
4279 Token::Ident(t) | Token::QuotedIdent(t) => t.clone(),
4280 Token::Tables => "tables".to_string(),
4281 _ => return None,
4284 };
4285 let (prefix, normalised) = if schema.eq_ignore_ascii_case("information_schema") {
4289 ("__spg_info_", tbl.to_ascii_lowercase())
4290 } else if schema.eq_ignore_ascii_case("pg_catalog") {
4291 let bare = tbl
4292 .to_ascii_lowercase()
4293 .strip_prefix("pg_")
4294 .map(alloc::string::String::from)
4295 .unwrap_or_else(|| tbl.to_ascii_lowercase());
4296 ("__spg_pg_", bare)
4297 } else if schema.eq_ignore_ascii_case("mysql") {
4298 ("__spg_mysql_", tbl.to_ascii_lowercase())
4302 } else {
4303 return None;
4304 };
4305 self.advance(); self.advance(); self.advance(); Some(alloc::format!("{prefix}{normalised}"))
4309 }
4310
4311 fn try_peek_meta_bare(&mut self) -> Option<String> {
4318 const PG_META_TABLES: &[&str] = &[
4319 "pg_attribute",
4320 "pg_class",
4321 "pg_constraint",
4322 "pg_database",
4323 "pg_extension",
4324 "pg_index",
4325 "pg_indexes",
4326 "pg_matviews",
4327 "pg_namespace",
4328 "pg_proc",
4329 "pg_roles",
4330 "pg_settings",
4331 "pg_trigger",
4332 "pg_type",
4333 "pg_user",
4334 "pg_views",
4335 ];
4336 let name = match self.tokens.get(self.pos) {
4337 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4338 _ => return None,
4339 };
4340 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4343 return None;
4344 }
4345 if !PG_META_TABLES.contains(&name.as_str()) {
4346 return None;
4347 }
4348 self.advance();
4349 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4350 Some(alloc::format!("__spg_pg_{bare}"))
4351 }
4352
4353 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4355 match self.advance() {
4356 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4357 other => Err(ParseError {
4358 message: format!("expected {kw:?}, got {other:?}"),
4359 token_pos: self.pos.saturating_sub(1),
4360 }),
4361 }
4362 }
4363
4364 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4368 match self.advance() {
4369 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4370 other => Err(ParseError {
4371 message: format!("expected identifier or string, got {other:?}"),
4372 token_pos: self.pos.saturating_sub(1),
4373 }),
4374 }
4375 }
4376
4377 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4378 match self.advance() {
4379 Token::String(s) => Ok(s),
4380 other => Err(ParseError {
4381 message: format!("expected quoted string, got {other:?}"),
4382 token_pos: self.pos.saturating_sub(1),
4383 }),
4384 }
4385 }
4386
4387 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4388 let mut head = self.parse_bare_select()?;
4393 while matches!(self.peek(), Token::Union) {
4394 self.advance();
4395 let kind = if matches!(self.peek(), Token::All) {
4396 self.advance();
4397 UnionKind::All
4398 } else {
4399 UnionKind::Distinct
4400 };
4401 let peer = self.parse_bare_select()?;
4402 head.unions.push((kind, peer));
4403 }
4404 head.order_by = if matches!(self.peek(), Token::Order) {
4405 self.advance();
4406 if !matches!(self.peek(), Token::By) {
4407 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4408 }
4409 self.advance();
4410 let mut keys = Vec::new();
4413 loop {
4414 let expr = self.parse_expr(0)?;
4415 let desc = if matches!(self.peek(), Token::Desc) {
4416 self.advance();
4417 true
4418 } else if matches!(self.peek(), Token::Asc) {
4419 self.advance();
4420 false
4421 } else {
4422 false
4423 };
4424 let nulls_first = self.parse_optional_nulls_placement()?;
4426 keys.push(OrderBy {
4427 expr,
4428 desc,
4429 nulls_first,
4430 });
4431 if matches!(self.peek(), Token::Comma) {
4432 self.advance();
4433 } else {
4434 break;
4435 }
4436 }
4437 keys
4438 } else {
4439 Vec::new()
4440 };
4441 head.limit = if matches!(self.peek(), Token::Limit) {
4442 self.advance();
4443 if self.consume_limit_unbounded_sentinel() {
4450 None
4451 } else {
4452 Some(self.parse_limit_expr("LIMIT")?)
4453 }
4454 } else {
4455 None
4456 };
4457 head.offset = if matches!(self.peek(), Token::Offset) {
4458 self.advance();
4459 let off = self.parse_limit_expr("OFFSET")?;
4463 self.consume_optional_rows_keyword();
4464 Some(off)
4465 } else {
4466 None
4467 };
4468 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4474 {
4475 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4478 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4479 {
4480 self.advance();
4481 }
4482 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4485 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4486 {
4487 crate::ast::LimitExpr::Literal(1)
4489 } else {
4490 self.parse_limit_expr("FETCH FIRST")?
4491 };
4492 self.consume_optional_rows_keyword();
4494 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4500 if s.eq_ignore_ascii_case("only"))
4501 {
4502 self.advance();
4503 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4504 if s.eq_ignore_ascii_case("with"))
4505 {
4506 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4508 if s.eq_ignore_ascii_case("ties"))
4509 {
4510 self.advance();
4511 head.limit_with_ties = true;
4512 }
4513 }
4514 head.limit = Some(count);
4515 }
4516 self.consume_optional_for_lock_clauses();
4531 Ok(Statement::Select(head))
4532 }
4533
4534 fn consume_optional_for_lock_clauses(&mut self) {
4541 while matches!(self.peek(), Token::For) {
4542 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4546 if s.eq_ignore_ascii_case("no"))
4547 {
4548 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4552 if s.eq_ignore_ascii_case("key"))
4553 {
4554 self.advance(); }
4556 }
4557 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4559 if s.eq_ignore_ascii_case("key"))
4560 {
4561 self.advance(); }
4563 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4569 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4570 {
4571 self.advance();
4572 } else {
4573 return;
4578 }
4579 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4582 if s.eq_ignore_ascii_case("of"))
4583 {
4584 self.advance(); #[allow(clippy::while_let_loop)]
4586 loop {
4587 match self.peek() {
4588 Token::Ident(_) | Token::QuotedIdent(_) => {
4589 self.advance();
4590 if matches!(self.peek(), Token::Dot) {
4592 self.advance();
4593 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4594 self.advance();
4595 }
4596 }
4597 }
4598 _ => break,
4599 }
4600 if matches!(self.peek(), Token::Comma) {
4601 self.advance();
4602 } else {
4603 break;
4604 }
4605 }
4606 }
4607 match self.peek().clone() {
4609 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4610 self.advance();
4611 }
4612 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4613 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4615 if s.eq_ignore_ascii_case("locked"))
4616 {
4617 self.advance(); }
4619 }
4620 _ => {}
4621 }
4622 }
4624 }
4625
4626 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4635 if matches!(self.peek(), Token::Null) {
4636 self.advance();
4637 return true;
4638 }
4639 if matches!(self.peek(), Token::All) {
4640 self.advance();
4641 return true;
4642 }
4643 false
4644 }
4645
4646 fn consume_optional_rows_keyword(&mut self) {
4650 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4651 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4652 {
4653 self.advance();
4654 }
4655 }
4656
4657 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4658 match self.advance() {
4659 Token::Integer(n) if n >= 0 => u32::try_from(n)
4660 .map(crate::ast::LimitExpr::Literal)
4661 .map_err(|_| ParseError {
4662 message: alloc::format!("{label} value too large: {n}"),
4663 token_pos: self.pos.saturating_sub(1),
4664 }),
4665 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4666 other => Err(ParseError {
4667 message: alloc::format!(
4668 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4669 ),
4670 token_pos: self.pos.saturating_sub(1),
4671 }),
4672 }
4673 }
4674
4675 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4680 if !matches!(self.peek(), Token::Select) {
4681 return Err(self.err(format!(
4682 "expected SELECT to start a query block, got {:?}",
4683 self.peek()
4684 )));
4685 }
4686 self.advance();
4687 let distinct = if matches!(self.peek(), Token::Distinct) {
4688 self.advance();
4689 true
4690 } else {
4691 false
4692 };
4693 let items = self.parse_select_list()?;
4694 let from = if matches!(self.peek(), Token::From) {
4695 self.advance();
4696 Some(self.parse_from_clause()?)
4697 } else {
4698 None
4699 };
4700 let where_ = if matches!(self.peek(), Token::Where) {
4701 self.advance();
4702 Some(self.parse_expr(0)?)
4703 } else {
4704 None
4705 };
4706 let mut group_by_all = false;
4707 let group_by = if matches!(self.peek(), Token::Group) {
4708 self.advance();
4709 if !matches!(self.peek(), Token::By) {
4710 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4711 }
4712 self.advance();
4713 if matches!(self.peek(), Token::All) {
4716 self.advance();
4717 group_by_all = true;
4718 None
4719 } else {
4720 let mut groups = Vec::new();
4721 loop {
4722 groups.push(self.parse_expr(0)?);
4723 if matches!(self.peek(), Token::Comma) {
4724 self.advance();
4725 } else {
4726 break;
4727 }
4728 }
4729 Some(groups)
4730 }
4731 } else {
4732 None
4733 };
4734 let having = if matches!(self.peek(), Token::Having) {
4735 self.advance();
4736 Some(self.parse_expr(0)?)
4737 } else {
4738 None
4739 };
4740 Ok(SelectStatement {
4741 ctes: Vec::new(),
4742 distinct,
4743 items,
4744 from,
4745 where_,
4746 group_by,
4747 group_by_all,
4748 having,
4749 unions: Vec::new(),
4750 order_by: Vec::new(),
4751 limit: None,
4752 offset: None,
4753 limit_with_ties: false,
4754 })
4755 }
4756
4757 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4758 debug_assert!(matches!(self.peek(), Token::Table));
4760 self.advance();
4761 let if_not_exists = self.consume_if_not_exists();
4762 let name = self.expect_ident_like()?;
4763 if !matches!(self.peek(), Token::LParen) {
4764 return Err(self.err(format!(
4765 "expected '(' after table name, got {:?}",
4766 self.peek()
4767 )));
4768 }
4769 self.advance();
4770 let mut columns = Vec::new();
4771 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4772 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4773 loop {
4774 if self.peek_table_level_pk_start() {
4780 table_constraints.push(self.parse_table_level_primary_key()?);
4781 } else if self.peek_table_level_unique_start() {
4782 table_constraints.push(self.parse_table_level_unique()?);
4783 } else if self.peek_table_level_check_start() {
4784 table_constraints.push(self.parse_table_level_check()?);
4786 } else if self.peek_mysql_inline_key_start() {
4787 if let Some(uc) = self.parse_mysql_inline_key()? {
4793 table_constraints.push(uc);
4794 }
4795 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
4796 self.advance(); let _name = self.expect_ident_like()?;
4804 table_constraints.push(match kind {
4805 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
4806 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
4807 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
4808 });
4809 } else if self.peek_constraint_or_fk_start() {
4810 foreign_keys.push(self.parse_table_level_fk()?);
4811 } else {
4812 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
4813 if col.is_unique {
4817 table_constraints.push(crate::ast::TableConstraint::Unique {
4818 name: None,
4819 columns: alloc::vec![col.name.clone()],
4820 nulls_not_distinct: false,
4821 });
4822 }
4823 if let Some(check_expr) = col.check.clone() {
4824 table_constraints.push(crate::ast::TableConstraint::Check {
4825 name: None,
4826 expr: check_expr,
4827 });
4828 }
4829 columns.push(col);
4830 if let Some(fk) = col_level_fk {
4831 foreign_keys.push(fk);
4832 }
4833 }
4834 match self.peek() {
4835 Token::Comma => {
4836 self.advance();
4837 }
4838 Token::RParen => {
4839 self.advance();
4840 break;
4841 }
4842 other => {
4843 return Err(
4844 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
4845 );
4846 }
4847 }
4848 }
4849 if columns.is_empty() {
4850 return Err(self.err("CREATE TABLE requires at least one column".into()));
4851 }
4852 self.consume_mysql_table_options();
4859 Ok(Statement::CreateTable(CreateTableStatement {
4860 name,
4861 columns,
4862 if_not_exists,
4863 foreign_keys,
4864 table_constraints,
4865 }))
4866 }
4867
4868 fn peek_mysql_inline_key_start(&self) -> bool {
4877 let cur = self.peek();
4878 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
4888 match self.tokens.get(skip) {
4891 Some(Token::LParen) => true,
4892 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
4893 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
4894 }
4895 _ => false,
4896 }
4897 };
4898 let is_key_or_index_tok = |t: &Token| -> bool {
4902 matches!(t, Token::Index)
4903 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
4904 };
4905 match cur {
4906 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
4907 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4908 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
4909 }
4910 Token::Ident(s)
4911 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
4912 {
4913 let nxt = self.tokens.get(self.pos + 1);
4914 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
4915 self.pos + 2
4916 } else {
4917 self.pos + 1
4918 };
4919 after_keyword_followed_by_paren_or_ident_paren(after_after)
4920 }
4921 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4922 let nxt = self.tokens.get(self.pos + 1);
4923 if !nxt.is_some_and(is_key_or_index_tok) {
4924 return false;
4925 }
4926 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
4927 }
4928 _ => false,
4929 }
4930 }
4931
4932 fn parse_mysql_inline_key(
4941 &mut self,
4942 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
4943 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
4945 {
4946 self.advance();
4947 true
4948 } else {
4949 false
4950 };
4951 let mut is_fulltext = false;
4957 let mut is_spatial = false;
4958 if let Token::Ident(s) = self.peek().clone() {
4959 if s.eq_ignore_ascii_case("fulltext") {
4960 self.advance();
4961 is_fulltext = true;
4962 } else if s.eq_ignore_ascii_case("spatial") {
4963 self.advance();
4964 is_spatial = true;
4965 }
4966 }
4967 match self.peek() {
4970 Token::Index => {
4971 self.advance();
4972 }
4973 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4974 self.advance();
4975 }
4976 other => {
4977 return Err(self.err(alloc::format!(
4978 "expected KEY/INDEX in inline index declaration, got {other:?}"
4979 )));
4980 }
4981 }
4982 let mut idx_name: Option<String> = None;
4987 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
4988 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4989 {
4990 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
4991 idx_name = Some(s);
4992 }
4993 }
4994 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4996 self.advance();
4997 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4998 self.advance();
4999 }
5000 }
5001 if !matches!(self.peek(), Token::LParen) {
5003 return Err(self.err(alloc::format!(
5004 "expected '(' in inline KEY/INDEX, got {:?}",
5005 self.peek()
5006 )));
5007 }
5008 self.advance();
5009 let mut cols: Vec<String> = Vec::new();
5010 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5011 self.advance();
5012 cols.push(s);
5013 if matches!(self.peek(), Token::LParen) {
5015 let mut depth = 1usize;
5016 self.advance();
5017 while depth > 0 {
5018 match self.peek() {
5019 Token::LParen => depth += 1,
5020 Token::RParen => depth -= 1,
5021 Token::Eof => break,
5022 _ => {}
5023 }
5024 self.advance();
5025 }
5026 }
5027 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5029 || matches!(self.peek(), Token::Asc | Token::Desc)
5030 {
5031 self.advance();
5032 }
5033 if matches!(self.peek(), Token::Comma) {
5034 self.advance();
5035 continue;
5036 }
5037 break;
5038 }
5039 if matches!(self.peek(), Token::RParen) {
5040 self.advance();
5041 }
5042 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5045 self.advance();
5046 }
5047 if cols.is_empty() {
5048 return Ok(None);
5049 }
5050 if is_unique {
5051 Ok(Some(crate::ast::TableConstraint::Unique {
5057 name: idx_name,
5058 columns: cols,
5059 nulls_not_distinct: false,
5060 }))
5061 } else if is_fulltext {
5062 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5068 name: idx_name,
5069 columns: cols,
5070 }))
5071 } else if is_spatial {
5072 Ok(None)
5075 } else {
5076 Ok(Some(crate::ast::TableConstraint::Index {
5079 name: idx_name,
5080 columns: cols,
5081 }))
5082 }
5083 }
5084
5085 fn consume_mysql_table_options(&mut self) {
5090 loop {
5091 let name_lc = match self.peek().clone() {
5095 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5096 Token::Default => alloc::string::String::from("default"),
5097 _ => break,
5098 };
5099 let known = matches!(
5100 name_lc.as_str(),
5101 "engine"
5102 | "default"
5103 | "charset"
5104 | "collate"
5105 | "auto_increment"
5106 | "row_format"
5107 | "comment"
5108 | "pack_keys"
5109 | "stats_persistent"
5110 | "stats_auto_recalc"
5111 | "stats_sample_pages"
5112 | "key_block_size"
5113 | "tablespace"
5114 | "min_rows"
5115 | "max_rows"
5116 | "checksum"
5117 | "delay_key_write"
5118 | "insert_method"
5119 | "data"
5120 | "index"
5121 | "encryption"
5122 | "compression"
5123 );
5124 if !known {
5125 break;
5126 }
5127 self.advance(); if name_lc == "default" {
5131 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5132 self.advance();
5133 }
5134 }
5135 if matches!(self.peek(), Token::Eq) {
5136 self.advance();
5137 }
5138 match self.peek() {
5139 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5140 self.advance();
5141 }
5142 _ => {}
5143 }
5144 }
5145 }
5146
5147 fn peek_table_level_pk_start(&self) -> bool {
5152 let cur = self.peek();
5153 let nxt = self.tokens.get(self.pos + 1);
5154 let nxt2 = self.tokens.get(self.pos + 2);
5155 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5156 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5157 let is_lparen = matches!(nxt2, Some(Token::LParen));
5158 is_primary && is_key && is_lparen
5159 }
5160
5161 fn peek_table_level_unique_start(&self) -> bool {
5165 let cur = self.peek();
5166 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5167 if !is_unique {
5168 return false;
5169 }
5170 let n1 = self.tokens.get(self.pos + 1);
5171 if matches!(n1, Some(Token::LParen)) {
5173 return true;
5174 }
5175 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5177 if !is_nulls {
5178 return false;
5179 }
5180 let n2 = self.tokens.get(self.pos + 2);
5181 let n3 = self.tokens.get(self.pos + 3);
5182 let n4 = self.tokens.get(self.pos + 4);
5183 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5185 return true;
5186 }
5187 if matches!(n2, Some(Token::Not))
5189 && matches!(n3, Some(Token::Distinct))
5190 && matches!(n4, Some(Token::LParen))
5191 {
5192 return true;
5193 }
5194 false
5195 }
5196
5197 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5198 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5201 Ok(crate::ast::TableConstraint::PrimaryKey {
5202 name: None,
5203 columns,
5204 })
5205 }
5206
5207 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5208 self.advance(); let mut nulls_not_distinct = false;
5213 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5214 let n1 = self.tokens.get(self.pos + 1);
5215 let n2 = self.tokens.get(self.pos + 2);
5216 let is_not = matches!(n1, Some(Token::Not));
5217 let is_distinct = matches!(n2, Some(Token::Distinct));
5218 if is_not && is_distinct {
5219 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5223 } else if matches!(n1, Some(Token::Distinct)) {
5224 self.advance(); self.advance(); }
5227 }
5228 let columns = self.parse_paren_ident_list("UNIQUE")?;
5229 Ok(crate::ast::TableConstraint::Unique {
5230 name: None,
5231 columns,
5232 nulls_not_distinct,
5233 })
5234 }
5235
5236 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5240 self.advance(); if !matches!(self.peek(), Token::LParen) {
5242 return Err(self.err(alloc::format!(
5243 "expected '(' after CHECK, got {:?}",
5244 self.peek()
5245 )));
5246 }
5247 self.advance();
5248 let expr = self.parse_expr(0)?;
5249 if !matches!(self.peek(), Token::RParen) {
5250 return Err(self.err(alloc::format!(
5251 "expected ')' to close CHECK predicate, got {:?}",
5252 self.peek()
5253 )));
5254 }
5255 self.advance();
5256 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5257 }
5258
5259 fn peek_table_level_check_start(&self) -> bool {
5261 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5262 }
5263
5264 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5269 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5270 return None;
5271 }
5272 match self.tokens.get(self.pos + 2) {
5275 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5276 Some(NamedTableConstraintKind::Check)
5277 }
5278 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5279 Some(NamedTableConstraintKind::Unique)
5280 }
5281 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5282 Some(NamedTableConstraintKind::PrimaryKey)
5283 }
5284 _ => None,
5285 }
5286 }
5287
5288 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5289 if !matches!(self.peek(), Token::LParen) {
5290 return Err(self.err(alloc::format!(
5291 "expected '(' after {ctx}, got {:?}",
5292 self.peek()
5293 )));
5294 }
5295 self.advance();
5296 let mut out = Vec::new();
5297 loop {
5298 out.push(self.expect_ident_like()?);
5299 match self.peek() {
5300 Token::Comma => {
5301 self.advance();
5302 }
5303 Token::RParen => {
5304 self.advance();
5305 break;
5306 }
5307 other => {
5308 return Err(self.err(alloc::format!(
5309 "expected ',' or ')' in {ctx} list, got {other:?}"
5310 )));
5311 }
5312 }
5313 }
5314 if out.is_empty() {
5315 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5316 }
5317 Ok(out)
5318 }
5319
5320 fn peek_constraint_or_fk_start(&self) -> bool {
5325 let is_constraint_kw = matches!(
5326 self.peek(),
5327 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5328 );
5329 let is_foreign_kw = matches!(
5330 self.peek(),
5331 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5332 );
5333 is_constraint_kw || is_foreign_kw
5334 }
5335
5336 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5340 let mut name: Option<String> = None;
5341 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5342 self.advance();
5343 name = Some(self.expect_ident_like()?);
5344 }
5345 match self.advance() {
5347 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5348 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5349 }
5350 match self.advance() {
5352 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5353 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5354 }
5355 if !matches!(self.peek(), Token::LParen) {
5357 return Err(self.err(format!(
5358 "expected '(' after FOREIGN KEY, got {:?}",
5359 self.peek()
5360 )));
5361 }
5362 self.advance();
5363 let mut columns = Vec::new();
5364 loop {
5365 columns.push(self.expect_ident_like()?);
5366 match self.peek() {
5367 Token::Comma => {
5368 self.advance();
5369 }
5370 Token::RParen => {
5371 self.advance();
5372 break;
5373 }
5374 other => {
5375 return Err(self.err(format!(
5376 "expected ',' or ')' in FK column list, got {other:?}"
5377 )));
5378 }
5379 }
5380 }
5381 if columns.is_empty() {
5382 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5383 }
5384 let (parent_table, parent_columns, on_delete, on_update) =
5385 self.parse_references_tail(columns.len())?;
5386 Ok(ForeignKeyConstraint {
5387 name,
5388 columns,
5389 parent_table,
5390 parent_columns,
5391 on_delete,
5392 on_update,
5393 })
5394 }
5395
5396 fn parse_references_tail(
5401 &mut self,
5402 expected_arity: usize,
5403 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5404 match self.advance() {
5405 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5406 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5407 }
5408 let parent_table = self.expect_ident_like()?;
5409 let mut parent_columns: Vec<String> = Vec::new();
5410 if matches!(self.peek(), Token::LParen) {
5411 self.advance();
5412 loop {
5413 parent_columns.push(self.expect_ident_like()?);
5414 match self.peek() {
5415 Token::Comma => {
5416 self.advance();
5417 }
5418 Token::RParen => {
5419 self.advance();
5420 break;
5421 }
5422 other => {
5423 return Err(self.err(format!(
5424 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5425 )));
5426 }
5427 }
5428 }
5429 }
5430 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5431 return Err(self.err(format!(
5432 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5433 expected_arity,
5434 parent_columns.len()
5435 )));
5436 }
5437 let mut on_delete = FkAction::Restrict;
5450 let mut on_update = FkAction::Restrict;
5451 let mut seen_on_delete = false;
5452 let mut seen_on_update = false;
5453 loop {
5454 let before = self.pos;
5456 self.consume_optional_deferrable_clauses()?;
5457 if self.pos != before {
5458 continue;
5459 }
5460 if !matches!(self.peek(), Token::On) {
5462 break;
5463 }
5464 self.advance();
5465 let which = self.advance();
5466 let action = self.parse_fk_action()?;
5467 match which {
5468 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5469 if seen_on_delete {
5470 return Err(self.err("ON DELETE specified twice".into()));
5471 }
5472 seen_on_delete = true;
5473 on_delete = action;
5474 }
5475 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5476 if seen_on_update {
5477 return Err(self.err("ON UPDATE specified twice".into()));
5478 }
5479 seen_on_update = true;
5480 on_update = action;
5481 }
5482 other => {
5483 return Err(
5484 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5485 );
5486 }
5487 }
5488 }
5489 Ok((parent_table, parent_columns, on_delete, on_update))
5490 }
5491
5492 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5495 match self.advance() {
5496 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5497 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5498 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5499 Token::Null => Ok(FkAction::SetNull),
5500 Token::Default => Ok(FkAction::SetDefault),
5501 other => Err(self.err(format!(
5502 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5503 ))),
5504 },
5505 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5506 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5507 other => Err(self.err(format!(
5508 "expected ACTION after NO in FK action, got {other:?}"
5509 ))),
5510 },
5511 other => Err(self.err(format!(
5512 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5513 ))),
5514 }
5515 }
5516
5517 fn consume_if_not_exists(&mut self) -> bool {
5520 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5524 if !looks_like_if {
5525 return false;
5526 }
5527 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5530 return false;
5531 }
5532 if !matches!(
5533 self.tokens.get(self.pos + 2),
5534 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5535 ) {
5536 return false;
5537 }
5538 self.advance(); self.advance(); self.advance(); true
5542 }
5543
5544 fn consume_if_exists(&mut self) -> bool {
5548 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5549 if !looks_like_if {
5550 return false;
5551 }
5552 if !matches!(
5553 self.tokens.get(self.pos + 1),
5554 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5555 ) {
5556 return false;
5557 }
5558 self.advance(); self.advance(); true
5561 }
5562
5563 fn parse_optional_nulls_placement(&mut self) -> Result<Option<bool>, ParseError> {
5571 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5572 return Ok(None);
5573 }
5574 self.advance();
5575 match self.advance() {
5576 Token::Ident(s) if s.eq_ignore_ascii_case("first") => Ok(Some(true)),
5577 Token::Ident(s) if s.eq_ignore_ascii_case("last") => Ok(Some(false)),
5578 other => Err(self.err(alloc::format!(
5579 "expected FIRST or LAST after NULLS, got {other:?}"
5580 ))),
5581 }
5582 }
5583
5584 fn consume_optional_index_column_qualifiers(&mut self) {
5585 loop {
5586 match self.peek() {
5587 Token::Asc | Token::Desc => {
5588 self.advance();
5589 }
5590 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5591 let look = self.tokens.get(self.pos + 1);
5592 if matches!(
5593 look,
5594 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5595 || k.eq_ignore_ascii_case("last")
5596 ) {
5597 self.advance();
5598 self.advance();
5599 } else {
5600 break;
5601 }
5602 }
5603 _ => break,
5604 }
5605 }
5606 }
5607
5608 fn parse_create_index_stmt_after_create(
5609 &mut self,
5610 is_unique: bool,
5611 ) -> Result<Statement, ParseError> {
5612 debug_assert!(matches!(self.peek(), Token::Index));
5614 self.advance();
5615 let if_not_exists = self.consume_if_not_exists();
5616 let name = self.expect_ident_like()?;
5617 if !matches!(self.peek(), Token::On) {
5618 return Err(self.err(format!(
5619 "expected ON after CREATE INDEX <name>, got {:?}",
5620 self.peek()
5621 )));
5622 }
5623 self.advance();
5624 let table = self.expect_ident_like()?;
5625 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5630 self.advance();
5631 let m = self.expect_ident_like()?;
5632 match m.to_ascii_lowercase().as_str() {
5633 "hnsw" => IndexMethod::Hnsw,
5634 "btree" => IndexMethod::BTree,
5635 "brin" => IndexMethod::Brin,
5636 "gin" => IndexMethod::Gin,
5641 "gist" | "spgist" | "hash" => IndexMethod::BTree,
5650 "ivfflat" => IndexMethod::Hnsw,
5658 other => {
5659 return Err(self.err(alloc::format!(
5660 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
5661 )));
5662 }
5663 }
5664 } else {
5665 IndexMethod::BTree
5666 };
5667 if !matches!(self.peek(), Token::LParen) {
5668 return Err(self.err(format!(
5669 "expected '(' before indexed column, got {:?}",
5670 self.peek()
5671 )));
5672 }
5673 self.advance();
5674 let mut opclass: Option<String> = None;
5683 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
5684 Token::Ident(s) | Token::QuotedIdent(s)
5691 if matches!(
5692 self.tokens.get(self.pos + 1),
5693 Some(Token::RParen | Token::Comma)
5694 ) =>
5695 {
5696 self.advance();
5697 (s, None)
5698 }
5699 Token::Ident(s) | Token::QuotedIdent(s)
5713 if matches!(
5714 self.tokens.get(self.pos + 1),
5715 Some(Token::Ident(_) | Token::QuotedIdent(_))
5716 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
5717 && matches!(
5718 self.tokens.get(self.pos + 3),
5719 Some(Token::Ident(op) | Token::QuotedIdent(op))
5720 if is_vector_opclass_name(op)
5721 ) =>
5722 {
5723 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
5727 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5728 opclass = Some(op.to_ascii_lowercase());
5729 }
5730 (s, None)
5731 }
5732 Token::Ident(s) | Token::QuotedIdent(s)
5733 if matches!(
5734 self.tokens.get(self.pos + 1),
5735 Some(Token::Ident(op) | Token::QuotedIdent(op))
5736 if is_vector_opclass_name(op)
5737 ) =>
5738 {
5739 self.advance(); let op_tok = self.advance();
5743 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5744 opclass = Some(op.to_ascii_lowercase());
5745 }
5746 (s, None)
5747 }
5748 Token::Ident(_) | Token::QuotedIdent(_) => {
5749 let key_expr = self.parse_expr(0)?;
5750 let primary = extract_first_column(&key_expr).ok_or_else(|| {
5751 self.err("expression index key must reference at least one column".into())
5752 })?;
5753 (primary, Some(key_expr))
5754 }
5755 other => {
5756 return Err(self.err(format!(
5757 "expected column ident or expression, got {other:?}"
5758 )));
5759 }
5760 };
5761 let mut extra_columns: Vec<String> = Vec::new();
5770 self.consume_optional_index_column_qualifiers();
5772 while matches!(self.peek(), Token::Comma) {
5773 self.advance();
5774 let extra = self.expect_ident_like()?;
5775 self.consume_optional_index_column_qualifiers();
5776 extra_columns.push(extra);
5777 }
5778 if !matches!(self.peek(), Token::RParen) {
5779 return Err(self.err(format!(
5780 "expected ')' after indexed column / expression, got {:?}",
5781 self.peek()
5782 )));
5783 }
5784 self.advance();
5785 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
5789 {
5790 self.advance();
5791 if !matches!(self.peek(), Token::LParen) {
5792 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
5793 }
5794 self.advance();
5795 let mut cols = Vec::new();
5796 loop {
5797 cols.push(self.expect_ident_like()?);
5798 match self.peek() {
5799 Token::Comma => {
5800 self.advance();
5801 }
5802 Token::RParen => {
5803 self.advance();
5804 break;
5805 }
5806 other => {
5807 return Err(self.err(format!(
5808 "expected ',' or ')' in INCLUDE list, got {other:?}"
5809 )));
5810 }
5811 }
5812 }
5813 cols
5814 } else {
5815 Vec::new()
5816 };
5817 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
5823 self.advance();
5824 if !matches!(self.peek(), Token::LParen) {
5825 return Err(self.err(format!(
5826 "expected '(' after WITH in CREATE INDEX, got {:?}",
5827 self.peek()
5828 )));
5829 }
5830 self.advance();
5831 loop {
5832 if matches!(self.peek(), Token::RParen) {
5833 self.advance();
5834 break;
5835 }
5836 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
5839 self.advance();
5840 let _ = self.advance(); }
5842 match self.peek() {
5843 Token::Comma => {
5844 self.advance();
5845 }
5846 Token::RParen => {
5847 self.advance();
5848 break;
5849 }
5850 other => {
5851 return Err(self.err(format!(
5852 "expected ',' or ')' in WITH (…) clause, got {other:?}"
5853 )));
5854 }
5855 }
5856 }
5857 }
5858 let partial_predicate = if matches!(self.peek(), Token::Where) {
5860 self.advance();
5861 Some(self.parse_expr(0)?)
5862 } else {
5863 None
5864 };
5865 if is_unique && !matches!(method, IndexMethod::BTree) {
5870 return Err(self.err(alloc::format!(
5871 "UNIQUE is only supported on BTree indexes, got USING {:?}",
5872 method
5873 )));
5874 }
5875 Ok(Statement::CreateIndex(CreateIndexStatement {
5876 name,
5877 table,
5878 column,
5879 method,
5880 if_not_exists,
5881 included_columns,
5882 partial_predicate,
5883 extra_columns: extra_columns.clone(),
5884 expression,
5885 is_unique,
5886 opclass,
5887 }))
5888 }
5889
5890 fn parse_column_def_with_fk(
5895 &mut self,
5896 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
5897 let col = self.parse_column_def()?;
5898 let inline_references = matches!(
5900 self.peek(),
5901 Token::Ident(s) if s.eq_ignore_ascii_case("references")
5902 );
5903 if !inline_references {
5904 return Ok((col, None));
5905 }
5906 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
5907 let fk = ForeignKeyConstraint {
5908 name: None,
5909 columns: vec![col.name.clone()],
5910 parent_table,
5911 parent_columns,
5912 on_delete,
5913 on_update,
5914 };
5915 Ok((col, Some(fk)))
5916 }
5917
5918 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
5926 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
5927 Ok(ty)
5928 }
5929
5930 #[allow(clippy::type_complexity)]
5931 fn parse_type_with_implied_flags(
5932 &mut self,
5933 ) -> Result<
5934 (
5935 ColumnTypeName,
5936 bool,
5937 bool,
5938 Option<String>,
5939 Collation,
5940 bool,
5941 Option<Vec<String>>,
5945 Option<Vec<String>>,
5948 ),
5949 ParseError,
5950 > {
5951 let mut ty_ident = match self.advance() {
5952 Token::Ident(s) => s,
5953 other => {
5954 return Err(ParseError {
5955 message: format!("expected column type, got {other:?}"),
5956 token_pos: self.pos.saturating_sub(1),
5957 });
5958 }
5959 };
5960 while matches!(self.peek(), Token::Dot) {
5965 self.advance();
5966 ty_ident = self.expect_ident_like()?;
5967 }
5968 let mut implied_auto_increment = false;
5969 let mut implied_not_null = false;
5970 let mut user_type_ref: Option<String> = None;
5971 let mut inline_enum_variants: Option<Vec<String>> = None;
5976 let mut inline_set_variants: Option<Vec<String>> = None;
5978 let mut ty = match ty_ident.as_str() {
5979 "smallserial" | "serial2" => {
5981 implied_auto_increment = true;
5982 implied_not_null = true;
5983 ColumnTypeName::SmallInt
5984 }
5985 "serial" | "serial4" => {
5986 implied_auto_increment = true;
5987 implied_not_null = true;
5988 ColumnTypeName::Int
5989 }
5990 "bigserial" | "serial8" => {
5991 implied_auto_increment = true;
5992 implied_not_null = true;
5993 ColumnTypeName::BigInt
5994 }
5995 "smallint" => {
6001 self.consume_optional_paren_size();
6006 ColumnTypeName::SmallInt
6007 }
6008 "tinyint" => {
6019 let width = self.peek_optional_paren_size_value();
6020 self.consume_optional_paren_size();
6021 if width == Some(1) {
6022 ColumnTypeName::Bool
6023 } else {
6024 ColumnTypeName::SmallInt
6025 }
6026 }
6027 "int" | "integer" | "mediumint" => {
6028 self.consume_optional_paren_size();
6029 ColumnTypeName::Int
6030 }
6031 "bigint" => {
6032 self.consume_optional_paren_size();
6033 ColumnTypeName::BigInt
6034 }
6035 "float" | "double" | "real" => {
6040 if ty_ident.eq_ignore_ascii_case("double")
6041 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6042 {
6043 self.advance();
6044 }
6045 ColumnTypeName::Float
6046 }
6047 "float4" | "float8" => ColumnTypeName::Float,
6049 "text" => ColumnTypeName::Text,
6050 "bool" | "boolean" => ColumnTypeName::Bool,
6051 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6052 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6053 "vector" => {
6054 let dim = self.parse_paren_size("VECTOR")?;
6055 let encoding = self.parse_optional_vector_encoding()?;
6056 ColumnTypeName::Vector { dim, encoding }
6057 }
6058 "numeric" => {
6059 let (precision, scale) = self.parse_optional_numeric_params()?;
6060 ColumnTypeName::Numeric(precision, scale)
6061 }
6062 "date" => ColumnTypeName::Date,
6063 "timestamp" | "datetime" => {
6066 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6072 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6073 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6074 {
6075 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6079 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6080 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6081 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6082 {
6083 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6087 } else {
6088 self.consume_optional_paren_size();
6092 ColumnTypeName::Timestamp
6093 }
6094 }
6095 "timestamptz" => ColumnTypeName::Timestamptz,
6099 "json" => ColumnTypeName::Json,
6104 "jsonb" => ColumnTypeName::Jsonb,
6105 "bytea" | "bytes" => ColumnTypeName::Bytes,
6111 "inet" | "cidr" | "macaddr" => ColumnTypeName::Text,
6121 "tsvector" => ColumnTypeName::TsVector,
6126 "tsquery" => ColumnTypeName::TsQuery,
6127 "uuid" => ColumnTypeName::Uuid,
6131 "time" => ColumnTypeName::Time,
6134 "year" => ColumnTypeName::Year,
6137 "timetz" => ColumnTypeName::TimeTz,
6140 "money" => ColumnTypeName::Money,
6143 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6145 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6146 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6147 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6148 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6149 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6150 "hstore" => ColumnTypeName::Hstore,
6152 "enum" => {
6158 if !matches!(self.peek(), Token::LParen) {
6160 return Err(self.err(alloc::format!(
6161 "expected '(' after ENUM, got {:?}",
6162 self.peek()
6163 )));
6164 }
6165 self.advance();
6166 let mut variants: Vec<String> = Vec::new();
6167 loop {
6168 match self.advance() {
6169 Token::String(s) => variants.push(s),
6170 other => {
6171 return Err(self.err(alloc::format!(
6172 "ENUM(...) expects string literal variants, got {other:?}"
6173 )));
6174 }
6175 }
6176 match self.peek() {
6177 Token::Comma => {
6178 self.advance();
6179 continue;
6180 }
6181 Token::RParen => {
6182 self.advance();
6183 break;
6184 }
6185 other => {
6186 return Err(self.err(alloc::format!(
6187 "expected ',' or ')' in ENUM(...), got {other:?}"
6188 )));
6189 }
6190 }
6191 }
6192 if variants.is_empty() {
6193 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6194 }
6195 inline_enum_variants = Some(variants);
6196 ColumnTypeName::Text
6199 }
6200 "set" => {
6204 if !matches!(self.peek(), Token::LParen) {
6205 return Err(self.err(alloc::format!(
6206 "expected '(' after SET, got {:?}",
6207 self.peek()
6208 )));
6209 }
6210 self.advance();
6211 let mut variants: Vec<String> = Vec::new();
6212 loop {
6213 match self.advance() {
6214 Token::String(s) => variants.push(s),
6215 other => {
6216 return Err(self.err(alloc::format!(
6217 "SET(...) expects string literal variants, got {other:?}"
6218 )));
6219 }
6220 }
6221 match self.peek() {
6222 Token::Comma => {
6223 self.advance();
6224 continue;
6225 }
6226 Token::RParen => {
6227 self.advance();
6228 break;
6229 }
6230 other => {
6231 return Err(self.err(alloc::format!(
6232 "expected ',' or ')' in SET(...), got {other:?}"
6233 )));
6234 }
6235 }
6236 }
6237 if variants.is_empty() {
6238 return Err(self.err("SET(...) must declare at least one variant".into()));
6239 }
6240 inline_set_variants = Some(variants);
6241 ColumnTypeName::Text
6242 }
6243 _other => {
6244 user_type_ref = Some(ty_ident.clone());
6250 ColumnTypeName::Text
6251 }
6252 };
6253 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6263 {
6264 self.advance();
6265 true
6266 } else {
6267 false
6268 };
6269 let mut collation = Collation::Binary;
6285 loop {
6286 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6287 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6288 {
6289 self.advance(); self.advance(); if matches!(
6292 self.peek(),
6293 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6294 ) {
6295 self.advance();
6296 }
6297 continue;
6298 }
6299 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6300 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6307 match this.peek().clone() {
6308 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6309 this.advance();
6310 Some(s)
6311 }
6312 Token::Default => {
6313 this.advance();
6314 Some(alloc::string::String::from("default"))
6315 }
6316 _ => None,
6317 }
6318 };
6319 let raw = if let Some(head) = read_collation_atom(self) {
6320 if matches!(self.peek(), Token::Dot) {
6322 self.advance();
6323 let tail = read_collation_atom(self).unwrap_or_default();
6324 alloc::format!("{head}.{tail}")
6325 } else {
6326 head
6327 }
6328 } else {
6329 alloc::string::String::new()
6330 };
6331 if !raw.is_empty() {
6332 let parsed = Collation::from_collation_name(&raw);
6333 if parsed != Collation::Binary {
6339 collation = parsed;
6340 }
6341 }
6342 continue;
6343 }
6344 break;
6345 }
6346 if matches!(self.peek(), Token::LBracket) {
6351 self.advance();
6352 if !matches!(self.peek(), Token::RBracket) {
6353 return Err(self.err(alloc::format!(
6354 "TEXT[] takes no dimension; got {:?}",
6355 self.peek()
6356 )));
6357 }
6358 self.advance();
6359 ty = match ty {
6363 ColumnTypeName::Text => ColumnTypeName::TextArray,
6364 ColumnTypeName::Int => ColumnTypeName::IntArray,
6365 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6366 other => {
6367 return Err(self.err(alloc::format!(
6368 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
6369 )));
6370 }
6371 };
6372 if matches!(self.peek(), Token::LBracket) {
6375 self.advance();
6376 if !matches!(self.peek(), Token::RBracket) {
6377 return Err(self.err(alloc::format!(
6378 "TYPE[][] second dimension takes no size; got {:?}",
6379 self.peek()
6380 )));
6381 }
6382 self.advance();
6383 ty = match ty {
6384 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6385 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6386 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6387 other => {
6388 return Err(self.err(alloc::format!(
6389 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6390 TEXT[][] only; got {other:?}"
6391 )));
6392 }
6393 };
6394 }
6395 }
6396 Ok((
6397 ty,
6398 implied_auto_increment,
6399 implied_not_null,
6400 user_type_ref,
6401 collation,
6402 is_unsigned,
6403 inline_enum_variants,
6404 inline_set_variants,
6405 ))
6406 }
6407
6408 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6409 if let Token::Ident(s) = self.peek()
6418 && [
6419 "unique",
6420 "primary",
6421 "foreign",
6422 "constraint",
6423 "check",
6424 "references",
6425 "exclude",
6426 ]
6427 .iter()
6428 .any(|kw| s.eq_ignore_ascii_case(kw))
6429 {
6430 return Err(self.err(alloc::format!(
6431 "unexpected reserved keyword '{s}' at start of column definition \
6432 (malformed table constraint?)"
6433 )));
6434 }
6435 let name = self.expect_ident_like()?;
6436 let (
6437 ty,
6438 implied_auto_increment,
6439 implied_not_null,
6440 user_type_ref,
6441 collation,
6442 is_unsigned,
6443 inline_enum_variants,
6444 inline_set_variants,
6445 ) = self.parse_type_with_implied_flags()?;
6446 let mut default: Option<Expr> = None;
6450 let mut nullable = !implied_not_null;
6451 let mut nullability_seen = implied_not_null;
6452 let mut auto_increment = implied_auto_increment;
6453 let mut is_primary_key = false;
6454 let mut is_unique = false;
6455 let mut check: Option<Expr> = None;
6456 let mut on_update_runtime: Option<Expr> = None;
6457 loop {
6458 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
6464 self.advance();
6465 let _name = self.expect_ident_like()?;
6466 continue;
6467 }
6468 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
6478 self.advance();
6479 match self.peek().clone() {
6480 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
6481 self.advance();
6482 }
6483 Token::By => {
6485 self.advance();
6486 if !matches!(self.peek(), Token::Default) {
6487 return Err(self.err(alloc::format!(
6488 "expected DEFAULT after GENERATED BY, got {:?}",
6489 self.peek()
6490 )));
6491 }
6492 self.advance();
6493 }
6494 other => {
6495 return Err(self.err(alloc::format!(
6496 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
6497 )));
6498 }
6499 }
6500 if !matches!(self.peek(), Token::As) {
6501 return Err(self.err(alloc::format!(
6502 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
6503 self.peek()
6504 )));
6505 }
6506 self.advance();
6507 if matches!(self.peek(), Token::LParen) {
6508 return Err(self.err(
6509 "generated expression columns (GENERATED … AS (expr) STORED) \
6510 are not supported"
6511 .into(),
6512 ));
6513 }
6514 self.expect_keyword_ident("identity")?;
6515 if matches!(self.peek(), Token::LParen) {
6519 let mut depth = 0usize;
6520 loop {
6521 match self.advance() {
6522 Token::LParen => depth += 1,
6523 Token::RParen => {
6524 depth -= 1;
6525 if depth == 0 {
6526 break;
6527 }
6528 }
6529 Token::Eof => {
6530 return Err(self.err(
6531 "unterminated sequence-options parens after IDENTITY".into(),
6532 ));
6533 }
6534 _ => {}
6535 }
6536 }
6537 }
6538 auto_increment = true;
6539 nullable = false;
6541 continue;
6542 }
6543 if matches!(self.peek(), Token::On)
6548 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6549 {
6550 self.advance(); self.advance(); let next = self.peek().clone();
6554 match next {
6555 Token::Ident(s) | Token::QuotedIdent(s)
6556 if s.eq_ignore_ascii_case("current_timestamp") =>
6557 {
6558 self.advance();
6559 if matches!(self.peek(), Token::LParen) {
6561 self.advance();
6562 if !matches!(self.peek(), Token::Integer(_)) {
6563 return Err(self.err(alloc::format!(
6564 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6565 self.peek()
6566 )));
6567 }
6568 self.advance();
6569 if !matches!(self.peek(), Token::RParen) {
6570 return Err(self.err(alloc::format!(
6571 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6572 self.peek()
6573 )));
6574 }
6575 self.advance();
6576 }
6577 on_update_runtime = Some(Expr::FunctionCall {
6578 name: "now".into(),
6579 args: Vec::new(),
6580 });
6581 continue;
6582 }
6583 other => {
6584 return Err(self.err(alloc::format!(
6585 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6586 )));
6587 }
6588 }
6589 }
6590 if matches!(self.peek(), Token::Default) {
6591 if default.is_some() {
6592 return Err(self.err("DEFAULT specified twice".into()));
6593 }
6594 self.advance();
6595 default = Some(self.parse_expr(0)?);
6596 continue;
6597 }
6598 if matches!(self.peek(), Token::Not) {
6599 if nullability_seen {
6600 return Err(self.err("NOT NULL specified twice".into()));
6601 }
6602 self.advance();
6603 if !matches!(self.peek(), Token::Null) {
6604 return Err(self.err(format!(
6605 "expected NULL after NOT in column def, got {:?}",
6606 self.peek()
6607 )));
6608 }
6609 self.advance();
6610 nullable = false;
6611 nullability_seen = true;
6612 continue;
6613 }
6614 if matches!(self.peek(), Token::Null) {
6620 if nullability_seen && !nullable {
6621 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6622 }
6623 self.advance();
6624 nullable = true;
6625 nullability_seen = true;
6626 continue;
6627 }
6628 if let Token::Ident(s) = self.peek()
6631 && (s.eq_ignore_ascii_case("auto_increment")
6632 || s.eq_ignore_ascii_case("autoincrement"))
6633 {
6634 if auto_increment {
6635 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6636 }
6637 self.advance();
6638 auto_increment = true;
6639 continue;
6640 }
6641 if let Token::Ident(s) = self.peek()
6646 && s.eq_ignore_ascii_case("primary")
6647 {
6648 if is_primary_key {
6649 return Err(self.err("PRIMARY KEY specified twice".into()));
6650 }
6651 let next = self.tokens.get(self.pos + 1);
6653 let next_is_key = matches!(
6654 next,
6655 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6656 );
6657 if !next_is_key {
6658 return Err(self.err(format!(
6659 "expected KEY after PRIMARY in column def, got {:?}",
6660 next
6661 )));
6662 }
6663 self.advance(); self.advance(); is_primary_key = true;
6666 if nullability_seen && nullable {
6667 return Err(self.err(
6668 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6669 ));
6670 }
6671 nullable = false;
6672 nullability_seen = true;
6673 continue;
6674 }
6675 if let Token::Ident(s) = self.peek()
6679 && s.eq_ignore_ascii_case("unique")
6680 {
6681 if is_unique {
6682 return Err(self.err("UNIQUE specified twice".into()));
6683 }
6684 self.advance();
6685 is_unique = true;
6686 continue;
6687 }
6688 if let Token::Ident(s) = self.peek()
6693 && s.eq_ignore_ascii_case("check")
6694 {
6695 self.advance();
6696 if !matches!(self.peek(), Token::LParen) {
6697 return Err(self.err(alloc::format!(
6698 "expected '(' after CHECK in column def, got {:?}",
6699 self.peek()
6700 )));
6701 }
6702 self.advance();
6703 let pred = self.parse_expr(0)?;
6704 if !matches!(self.peek(), Token::RParen) {
6705 return Err(self.err(alloc::format!(
6706 "expected ')' to close CHECK predicate, got {:?}",
6707 self.peek()
6708 )));
6709 }
6710 self.advance();
6711 check = Some(match check.take() {
6712 Some(prev) => Expr::Binary {
6713 op: BinOp::And,
6714 lhs: Box::new(prev),
6715 rhs: Box::new(pred),
6716 },
6717 None => pred,
6718 });
6719 continue;
6720 }
6721 break;
6722 }
6723 Ok(ColumnDef {
6724 name,
6725 ty,
6726 nullable,
6727 default,
6728 auto_increment,
6729 is_primary_key,
6730 is_unique,
6731 check,
6732 user_type_ref,
6733 on_update_runtime,
6734 collation,
6735 is_unsigned,
6736 inline_enum_variants,
6737 inline_set_variants,
6738 })
6739 }
6740
6741 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6745 if !matches!(self.peek(), Token::LParen) {
6746 return Ok((0, 0));
6750 }
6751 self.advance();
6752 let precision = match self.advance() {
6753 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6754 other => {
6755 return Err(ParseError {
6756 message: format!(
6757 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6758 ),
6759 token_pos: self.pos.saturating_sub(1),
6760 });
6761 }
6762 };
6763 let scale = if matches!(self.peek(), Token::Comma) {
6764 self.advance();
6765 match self.advance() {
6766 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6767 u8::try_from(n).expect("range-checked")
6768 }
6769 other => {
6770 return Err(ParseError {
6771 message: format!(
6772 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6773 ),
6774 token_pos: self.pos.saturating_sub(1),
6775 });
6776 }
6777 }
6778 } else {
6779 0
6780 };
6781 if !matches!(self.peek(), Token::RParen) {
6782 return Err(self.err(format!(
6783 "expected ')' to close NUMERIC params, got {:?}",
6784 self.peek()
6785 )));
6786 }
6787 self.advance();
6788 Ok((precision, scale))
6789 }
6790
6791 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6799 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6800 return Ok(VecEncoding::F32);
6801 }
6802 let n1 = self.tokens.get(self.pos + 1);
6808 let next_is_encoding = matches!(
6809 n1,
6810 Some(Token::Ident(s))
6811 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6812 );
6813 if !next_is_encoding {
6814 return Ok(VecEncoding::F32);
6815 }
6816 self.advance();
6817 let enc_ident = match self.advance() {
6818 Token::Ident(s) => s,
6819 other => {
6820 return Err(self.err(format!(
6821 "expected vector encoding after USING, got {other:?}"
6822 )));
6823 }
6824 };
6825 match enc_ident.to_ascii_lowercase().as_str() {
6826 "sq8" => Ok(VecEncoding::Sq8),
6827 "half" => Ok(VecEncoding::F16),
6830 other => Err(self.err(format!(
6831 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6832 ))),
6833 }
6834 }
6835
6836 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6842 if !matches!(self.peek(), Token::LParen) {
6843 return None;
6844 }
6845 let next = self.tokens.get(self.pos + 1)?;
6846 let n = match next {
6847 Token::Integer(n) => *n,
6848 _ => return None,
6849 };
6850 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6851 return None;
6852 }
6853 Some(n)
6854 }
6855
6856 fn consume_optional_paren_size(&mut self) {
6860 if !matches!(self.peek(), Token::LParen) {
6861 return;
6862 }
6863 self.advance();
6864 let mut depth = 1usize;
6866 while depth > 0 {
6867 match self.peek() {
6868 Token::LParen => depth += 1,
6869 Token::RParen => depth -= 1,
6870 Token::Eof => return,
6871 _ => {}
6872 }
6873 self.advance();
6874 }
6875 }
6876
6877 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6878 if !matches!(self.peek(), Token::LParen) {
6879 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6880 }
6881 self.advance();
6882 let n = match self.advance() {
6883 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6884 message: format!("{label} size too large: {n}"),
6885 token_pos: self.pos.saturating_sub(1),
6886 })?,
6887 other => {
6888 return Err(ParseError {
6889 message: format!("expected positive integer {label} size, got {other:?}"),
6890 token_pos: self.pos.saturating_sub(1),
6891 });
6892 }
6893 };
6894 if !matches!(self.peek(), Token::RParen) {
6895 return Err(self.err(format!(
6896 "expected ')' after {label} size, got {:?}",
6897 self.peek()
6898 )));
6899 }
6900 self.advance();
6901 Ok(n)
6902 }
6903
6904 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6905 debug_assert!(matches!(self.peek(), Token::Insert));
6906 self.advance();
6907 if !matches!(self.peek(), Token::Into) {
6908 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6909 }
6910 self.advance();
6911 let table = self.expect_ident_like()?;
6912 let columns = if matches!(self.peek(), Token::LParen) {
6914 self.advance();
6915 let mut names = Vec::new();
6916 loop {
6917 names.push(self.expect_ident_like()?);
6918 match self.peek() {
6919 Token::Comma => {
6920 self.advance();
6921 }
6922 Token::RParen => {
6923 self.advance();
6924 break;
6925 }
6926 other => {
6927 return Err(self.err(format!(
6928 "expected ',' or ')' in INSERT column list, got {other:?}"
6929 )));
6930 }
6931 }
6932 }
6933 Some(names)
6934 } else {
6935 None
6936 };
6937 if matches!(self.peek(), Token::Select) {
6940 let select_stmt = match self.parse_select_stmt()? {
6941 Statement::Select(s) => s,
6942 other => {
6943 return Err(self.err(alloc::format!(
6944 "expected SELECT after INSERT INTO ... target, got {other:?}"
6945 )));
6946 }
6947 };
6948 let on_conflict = self.parse_optional_on_conflict()?;
6949 let returning = self.parse_optional_returning()?;
6950 return Ok(Statement::Insert(InsertStatement {
6951 table,
6952 columns,
6953 rows: Vec::new(),
6954 select_source: Some(Box::new(select_stmt)),
6955 on_conflict,
6956 returning,
6957 }));
6958 }
6959 if !matches!(self.peek(), Token::Values) {
6960 return Err(self.err(format!(
6961 "expected VALUES or SELECT after table name, got {:?}",
6962 self.peek()
6963 )));
6964 }
6965 self.advance();
6966 if !matches!(self.peek(), Token::LParen) {
6967 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
6968 }
6969 let mut rows = Vec::new();
6970 loop {
6971 if !matches!(self.peek(), Token::LParen) {
6973 return Err(self.err(format!(
6974 "expected '(' for next VALUES tuple, got {:?}",
6975 self.peek()
6976 )));
6977 }
6978 self.advance();
6979 let mut tuple = Vec::new();
6980 loop {
6981 tuple.push(self.parse_expr(0)?);
6982 match self.peek() {
6983 Token::Comma => {
6984 self.advance();
6985 }
6986 Token::RParen => {
6987 self.advance();
6988 break;
6989 }
6990 other => {
6991 return Err(self.err(format!(
6992 "expected ',' or ')' in VALUES tuple, got {other:?}"
6993 )));
6994 }
6995 }
6996 }
6997 if tuple.is_empty() {
6998 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
6999 }
7000 rows.push(tuple);
7001 if matches!(self.peek(), Token::Comma) {
7003 self.advance();
7004 } else {
7005 break;
7006 }
7007 }
7008 let on_conflict = self.parse_optional_on_conflict()?;
7009 let returning = self.parse_optional_returning()?;
7010 Ok(Statement::Insert(InsertStatement {
7011 table,
7012 columns,
7013 rows,
7014 select_source: None,
7015 on_conflict,
7016 returning,
7017 }))
7018 }
7019
7020 fn parse_optional_on_conflict(
7025 &mut self,
7026 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7027 if !matches!(self.peek(), Token::On) {
7028 return Ok(None);
7029 }
7030 let next_is_conflict = matches!(
7033 self.tokens.get(self.pos + 1),
7034 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7035 );
7036 if !next_is_conflict {
7037 return Ok(None);
7038 }
7039 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7043 if matches!(self.peek(), Token::LParen) {
7044 self.advance();
7045 loop {
7046 target_columns.push(self.expect_ident_like()?);
7047 match self.peek() {
7048 Token::Comma => {
7049 self.advance();
7050 }
7051 Token::RParen => {
7052 self.advance();
7053 break;
7054 }
7055 other => {
7056 return Err(self.err(alloc::format!(
7057 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7058 )));
7059 }
7060 }
7061 }
7062 }
7063 match self.advance() {
7065 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7066 other => {
7067 return Err(self.err(alloc::format!(
7068 "expected DO after ON CONFLICT [(…)], got {other:?}"
7069 )));
7070 }
7071 }
7072 let action = match self.advance() {
7074 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7075 crate::ast::OnConflictAction::Nothing
7076 }
7077 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7078 self.parse_on_conflict_update_action()?
7079 }
7080 other => {
7081 return Err(self.err(alloc::format!(
7082 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7083 )));
7084 }
7085 };
7086 Ok(Some(crate::ast::OnConflictClause {
7087 target_columns,
7088 action,
7089 }))
7090 }
7091
7092 fn parse_on_conflict_update_action(
7096 &mut self,
7097 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7098 match self.advance() {
7100 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7101 other => {
7102 return Err(self.err(alloc::format!(
7103 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7104 )));
7105 }
7106 }
7107 let mut assignments: Vec<(String, Expr)> = Vec::new();
7108 loop {
7109 let col = self.expect_ident_like()?;
7110 if !matches!(self.peek(), Token::Eq) {
7111 return Err(self.err(alloc::format!(
7112 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7113 self.peek()
7114 )));
7115 }
7116 self.advance();
7117 let value = self.parse_expr(0)?;
7118 assignments.push((col, value));
7119 if matches!(self.peek(), Token::Comma) {
7120 self.advance();
7121 continue;
7122 }
7123 break;
7124 }
7125 let where_ = if matches!(self.peek(), Token::Where) {
7126 self.advance();
7127 Some(self.parse_expr(0)?)
7128 } else {
7129 None
7130 };
7131 Ok(crate::ast::OnConflictAction::Update {
7132 assignments,
7133 where_,
7134 })
7135 }
7136
7137 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7138 let mut items = Vec::new();
7139 loop {
7140 items.push(self.parse_select_item()?);
7141 if matches!(self.peek(), Token::Comma) {
7142 self.advance();
7143 } else {
7144 break;
7145 }
7146 }
7147 Ok(items)
7148 }
7149
7150 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7151 if matches!(self.peek(), Token::Star) {
7152 self.advance();
7153 return Ok(SelectItem::Wildcard);
7154 }
7155 let expr = self.parse_expr(0)?;
7156 let alias = self.parse_optional_alias();
7157 Ok(SelectItem::Expr { expr, alias })
7158 }
7159
7160 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7161 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7167 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7168 {
7169 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7173 Statement::Select(s) => s,
7174 other => {
7175 return Err(self.err(alloc::format!(
7176 "expected SELECT inside LATERAL ( … ), got {other:?}"
7177 )));
7178 }
7179 };
7180 if !matches!(self.peek(), Token::RParen) {
7181 return Err(self.err(alloc::format!(
7182 "expected ')' after LATERAL subquery, got {:?}",
7183 self.peek()
7184 )));
7185 }
7186 self.advance();
7187 let alias_ident = self.parse_optional_alias();
7188 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7189 return Ok(TableRef {
7190 name,
7191 alias: alias_ident,
7192 as_of_segment: None,
7193 unnest_expr: None,
7194 unnest_column_aliases: Vec::new(),
7195 generate_series_args: None,
7196 lateral_subquery: Some(Box::new(inner)),
7197 });
7198 }
7199 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
7203 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7204 {
7205 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
7208 if !matches!(self.peek(), Token::RParen) {
7209 return Err(self.err(alloc::format!(
7210 "expected ')' after unnest() argument, got {:?}",
7211 self.peek()
7212 )));
7213 }
7214 self.advance();
7215 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
7216 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
7217 return Ok(TableRef {
7218 name,
7219 alias: alias_ident,
7220 as_of_segment: None,
7221 unnest_expr: Some(Box::new(expr)),
7222 unnest_column_aliases,
7223 generate_series_args: None,
7224 lateral_subquery: None,
7225 });
7226 }
7227 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
7237 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7238 {
7239 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
7242 loop {
7243 args.push(self.parse_expr(0)?);
7244 if matches!(self.peek(), Token::Comma) {
7245 self.advance();
7246 continue;
7247 }
7248 break;
7249 }
7250 if !matches!(self.peek(), Token::RParen) {
7251 return Err(self.err(alloc::format!(
7252 "expected ')' after generate_series() arguments, got {:?}",
7253 self.peek()
7254 )));
7255 }
7256 self.advance();
7257 if args.len() < 2 || args.len() > 3 {
7258 return Err(self.err(alloc::format!(
7259 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
7260 args.len()
7261 )));
7262 }
7263 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7264 let name = alias_ident
7265 .clone()
7266 .unwrap_or_else(|| "generate_series".to_string());
7267 return Ok(TableRef {
7268 name,
7269 alias: alias_ident,
7270 as_of_segment: None,
7271 unnest_expr: None,
7272 unnest_column_aliases: Vec::new(),
7273 generate_series_args: Some(args),
7274 lateral_subquery: None,
7275 });
7276 }
7277 let name = if let Some(synth) = self.try_peek_meta_qualified() {
7286 synth
7287 } else if let Some(synth) = self.try_peek_meta_bare() {
7288 synth
7289 } else {
7290 self.expect_ident_like()?
7291 };
7292 let as_of_segment = if matches!(self.peek(), Token::As)
7298 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
7299 {
7300 self.advance(); self.advance(); let kw = match self.peek().clone() {
7303 Token::Ident(s) | Token::QuotedIdent(s) => s,
7304 other => {
7305 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
7306 }
7307 };
7308 if !kw.eq_ignore_ascii_case("segment") {
7309 return Err(self.err(format!(
7310 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
7311 )));
7312 }
7313 self.advance();
7314 let id = match self.advance() {
7317 Token::String(s) => s
7318 .parse::<u32>()
7319 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7320 Token::Integer(n) => u32::try_from(n)
7321 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7322 other => {
7323 return Err(self.err(format!(
7324 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7325 )));
7326 }
7327 };
7328 Some(id)
7329 } else {
7330 None
7331 };
7332 let alias = self.parse_optional_alias();
7333 Ok(TableRef {
7334 name,
7335 alias,
7336 as_of_segment,
7337 unnest_expr: None,
7338 unnest_column_aliases: Vec::new(),
7339 generate_series_args: None,
7340 lateral_subquery: None,
7341 })
7342 }
7343
7344 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
7350 let alias = self.parse_optional_alias();
7351 if alias.is_none() {
7352 return (None, Vec::new());
7353 }
7354 let mut cols: Vec<String> = Vec::new();
7355 if matches!(self.peek(), Token::LParen) {
7356 self.advance();
7357 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7358 self.advance();
7359 cols.push(s);
7360 if matches!(self.peek(), Token::Comma) {
7361 self.advance();
7362 continue;
7363 }
7364 break;
7365 }
7366 if matches!(self.peek(), Token::RParen) {
7367 self.advance();
7368 }
7369 }
7370 (alias, cols)
7371 }
7372
7373 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7378 let primary = self.parse_table_ref()?;
7379 let mut joins = Vec::new();
7380 loop {
7381 if matches!(self.peek(), Token::Comma) {
7383 self.advance();
7384 let table = self.parse_table_ref()?;
7385 joins.push(FromJoin {
7386 kind: JoinKind::Cross,
7387 table,
7388 on: None,
7389 });
7390 continue;
7391 }
7392 let kind =
7395 match self.peek() {
7396 Token::Inner => {
7397 self.advance();
7398 if !matches!(self.peek(), Token::Join) {
7399 return Err(self
7400 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7401 }
7402 self.advance();
7403 JoinKind::Inner
7404 }
7405 Token::Left => {
7406 self.advance();
7407 if matches!(self.peek(), Token::Outer) {
7408 self.advance();
7409 }
7410 if !matches!(self.peek(), Token::Join) {
7411 return Err(self.err(format!(
7412 "expected JOIN after LEFT [OUTER], got {:?}",
7413 self.peek()
7414 )));
7415 }
7416 self.advance();
7417 JoinKind::Left
7418 }
7419 Token::Cross => {
7420 self.advance();
7421 if !matches!(self.peek(), Token::Join) {
7422 return Err(self
7423 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7424 }
7425 self.advance();
7426 JoinKind::Cross
7427 }
7428 Token::Join => {
7429 self.advance();
7430 JoinKind::Inner
7431 }
7432 _ => break,
7433 };
7434 let table = self.parse_table_ref()?;
7435 let on = if matches!(self.peek(), Token::On) {
7436 self.advance();
7437 Some(self.parse_expr(0)?)
7438 } else if kind == JoinKind::Cross {
7439 None
7440 } else {
7441 return Err(self.err(format!(
7442 "expected ON after {:?} JOIN, got {:?}",
7443 kind,
7444 self.peek()
7445 )));
7446 };
7447 joins.push(FromJoin { kind, table, on });
7448 }
7449 Ok(FromClause { primary, joins })
7450 }
7451
7452 fn parse_optional_alias(&mut self) -> Option<String> {
7457 if matches!(self.peek(), Token::As) {
7458 self.advance();
7459 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7464 return self.expect_ident_like().ok();
7465 }
7466 return None;
7467 }
7468 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7476 if is_alias_stopword(s) {
7477 return None;
7478 }
7479 return self.expect_ident_like().ok();
7480 }
7481 None
7482 }
7483
7484 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7486 let mut lhs = self.parse_unary()?;
7487 while let Some((op, prec)) = binop_from(self.peek()) {
7488 if prec < min_prec {
7489 break;
7490 }
7491 self.advance();
7492 let any_kind = match self.peek() {
7497 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7498 Some(false)
7499 }
7500 Token::Ident(s) | Token::QuotedIdent(s)
7501 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7502 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7503 {
7504 Some(s.eq_ignore_ascii_case("any"))
7505 }
7506 _ => None,
7507 };
7508 if let Some(is_any) = any_kind {
7509 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7512 if !matches!(self.peek(), Token::RParen) {
7513 return Err(self.err(alloc::format!(
7514 "expected ')' after ANY/ALL argument, got {:?}",
7515 self.peek()
7516 )));
7517 }
7518 self.advance();
7519 lhs = Expr::AnyAll {
7520 expr: Box::new(lhs),
7521 op,
7522 array: Box::new(arr),
7523 is_any,
7524 };
7525 continue;
7526 }
7527 let rhs = self.parse_expr(prec + 1)?;
7528 lhs = Expr::Binary {
7529 lhs: Box::new(lhs),
7530 op,
7531 rhs: Box::new(rhs),
7532 };
7533 }
7534 Ok(lhs)
7535 }
7536
7537 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7538 match self.peek() {
7539 Token::Not => {
7540 self.advance();
7541 let e = self.parse_expr(3)?;
7544 Ok(Expr::Unary {
7545 op: UnOp::Not,
7546 expr: Box::new(e),
7547 })
7548 }
7549 Token::Minus => {
7550 self.advance();
7551 let e = self.parse_expr(8)?;
7554 Ok(Expr::Unary {
7555 op: UnOp::Neg,
7556 expr: Box::new(e),
7557 })
7558 }
7559 Token::Tilde => {
7560 self.advance();
7561 let e = self.parse_expr(8)?;
7563 Ok(Expr::Unary {
7564 op: UnOp::BitNot,
7565 expr: Box::new(e),
7566 })
7567 }
7568 _ => self.parse_atom(),
7569 }
7570 }
7571
7572 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7573 let tok_pos = self.pos;
7574 match self.advance() {
7575 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7576 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7577 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7578 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7579 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7580 Token::Null => Ok(Expr::Literal(Literal::Null)),
7581 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7585 Token::LParen => {
7586 if matches!(self.peek(), Token::Select) {
7590 let inner = self.parse_select_stmt()?;
7591 match self.advance() {
7592 Token::RParen => {
7593 let Statement::Select(s) = inner else {
7594 unreachable!("parse_select_stmt returns Select")
7595 };
7596 Ok(Expr::ScalarSubquery(Box::new(s)))
7597 }
7598 other => Err(ParseError {
7599 message: format!("expected ')' after scalar subquery, got {other:?}"),
7600 token_pos: self.pos.saturating_sub(1),
7601 }),
7602 }
7603 } else {
7604 let e = self.parse_expr(0)?;
7605 match self.advance() {
7606 Token::RParen => Ok(e),
7607 other => Err(ParseError {
7608 message: format!("expected ')', got {other:?}"),
7609 token_pos: self.pos.saturating_sub(1),
7610 }),
7611 }
7612 }
7613 }
7614 Token::LBracket => self.parse_vector_literal_body(),
7615 Token::Extract => self.parse_extract_atom(),
7616 Token::Interval => self.parse_interval_atom(),
7617 Token::Left if matches!(self.peek(), Token::LParen) => {
7624 self.advance(); let mut args = Vec::new();
7626 if !matches!(self.peek(), Token::RParen) {
7627 loop {
7628 args.push(self.parse_expr(0)?);
7629 match self.peek() {
7630 Token::Comma => {
7631 self.advance();
7632 }
7633 Token::RParen => break,
7634 other => {
7635 return Err(self.err(alloc::format!(
7636 "expected ',' or ')' in left() args, got {other:?}"
7637 )));
7638 }
7639 }
7640 }
7641 }
7642 self.advance(); Ok(Expr::FunctionCall {
7644 name: "left".into(),
7645 args,
7646 })
7647 }
7648 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7653 self.parse_exists_atom(false)
7654 }
7655 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7659 self.parse_case_atom()
7660 }
7661 Token::Ident(s) | Token::QuotedIdent(s)
7665 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7666 {
7667 self.advance(); let mut items: Vec<Expr> = Vec::new();
7669 if !matches!(self.peek(), Token::RBracket) {
7670 loop {
7671 items.push(self.parse_expr(0)?);
7672 match self.peek() {
7673 Token::Comma => {
7674 self.advance();
7675 }
7676 Token::RBracket => break,
7677 other => {
7678 return Err(self.err(alloc::format!(
7679 "expected ',' or ']' in ARRAY literal, got {other:?}"
7680 )));
7681 }
7682 }
7683 }
7684 }
7685 self.advance(); Ok(Expr::Array(items))
7687 }
7688 Token::Ident(s) | Token::QuotedIdent(s)
7703 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7704 {
7705 self.parse_match_against_atom()
7706 }
7707 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7708 other => Err(ParseError {
7709 message: format!("unexpected token {other:?} in expression"),
7710 token_pos: tok_pos,
7711 }),
7712 }
7713 .and_then(|atom| self.finish_postfix_casts(atom))
7715 }
7716
7717 fn parse_cast_target(&mut self) -> Result<CastTarget, ParseError> {
7722 let target = match self.advance() {
7723 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7724 "int" | "integer" | "int4" => {
7725 if matches!(self.peek(), Token::LBracket)
7726 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7727 {
7728 self.advance();
7729 self.advance();
7730 CastTarget::IntArray
7731 } else {
7732 CastTarget::Int
7733 }
7734 }
7735 "bigint" | "int8" => {
7736 if matches!(self.peek(), Token::LBracket)
7737 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7738 {
7739 self.advance();
7740 self.advance();
7741 CastTarget::BigIntArray
7742 } else {
7743 CastTarget::BigInt
7744 }
7745 }
7746 "float" | "double" | "real" => CastTarget::Float,
7747 "text" => {
7748 if matches!(self.peek(), Token::LBracket)
7750 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7751 {
7752 self.advance();
7753 self.advance();
7754 CastTarget::TextArray
7755 } else {
7756 CastTarget::Text
7757 }
7758 }
7759 "bool" | "boolean" => CastTarget::Bool,
7760 "vector" => CastTarget::Vector,
7761 "date" => CastTarget::Date,
7762 "timestamp" | "datetime" => CastTarget::Timestamp,
7763 "timestamptz" => CastTarget::Timestamptz,
7764 "interval" => CastTarget::Interval,
7765 "json" => CastTarget::Json,
7766 "jsonb" => CastTarget::Jsonb,
7767 "regtype" => CastTarget::RegType,
7768 "regclass" => CastTarget::RegClass,
7769 "tsvector" => CastTarget::TsVector,
7773 "tsquery" => CastTarget::TsQuery,
7774 "uuid" => CastTarget::Uuid,
7777 "bytea" => CastTarget::Bytea,
7782 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7787 other => {
7788 return Err(ParseError {
7789 message: format!("unsupported cast target `::{other}`"),
7790 token_pos: self.pos.saturating_sub(1),
7791 });
7792 }
7793 },
7794 Token::Interval => CastTarget::Interval,
7795 other => {
7796 return Err(ParseError {
7797 message: format!("expected type ident after `::`, got {other:?}"),
7798 token_pos: self.pos.saturating_sub(1),
7799 });
7800 }
7801 };
7802 Ok(target)
7803 }
7804
7805 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7806 loop {
7807 if matches!(self.peek(), Token::DoubleColon) {
7808 self.advance();
7809 let target = self.parse_cast_target()?;
7814 expr = Expr::Cast {
7815 expr: Box::new(expr),
7816 target,
7817 };
7818 continue;
7819 }
7820 if matches!(self.peek(), Token::Is) {
7821 self.advance();
7822 let negated = if matches!(self.peek(), Token::Not) {
7823 self.advance();
7824 true
7825 } else {
7826 false
7827 };
7828 if matches!(self.peek(), Token::Distinct) {
7831 self.advance();
7832 if !matches!(self.peek(), Token::From) {
7833 return Err(self.err(format!(
7834 "expected FROM after IS{} DISTINCT, got {:?}",
7835 if negated { " NOT" } else { "" },
7836 self.peek()
7837 )));
7838 }
7839 self.advance();
7840 let rhs = self.parse_expr(20)?;
7844 let op = if negated {
7845 BinOp::IsNotDistinctFrom
7846 } else {
7847 BinOp::IsDistinctFrom
7848 };
7849 expr = Expr::Binary {
7850 op,
7851 lhs: Box::new(expr),
7852 rhs: Box::new(rhs),
7853 };
7854 continue;
7855 }
7856 if !matches!(self.peek(), Token::Null) {
7857 return Err(self.err(format!(
7858 "expected NULL or DISTINCT after IS{}, got {:?}",
7859 if negated { " NOT" } else { "" },
7860 self.peek()
7861 )));
7862 }
7863 self.advance();
7864 expr = Expr::IsNull {
7865 expr: Box::new(expr),
7866 negated,
7867 };
7868 continue;
7869 }
7870 let negated = if matches!(self.peek(), Token::Not) {
7874 let next = self.tokens.get(self.pos + 1);
7875 matches!(next, Some(Token::Between | Token::In | Token::Like))
7876 || matches!(next, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("ilike"))
7877 } else {
7878 false
7879 };
7880 if negated {
7881 self.advance();
7882 }
7883 if matches!(self.peek(), Token::Between) {
7884 expr = self.parse_between_tail(expr, negated)?;
7885 continue;
7886 }
7887 if matches!(self.peek(), Token::In) {
7888 expr = self.parse_in_tail(expr, negated)?;
7889 continue;
7890 }
7891 if matches!(self.peek(), Token::Like) {
7892 self.advance();
7893 let pattern = self.parse_expr(5)?;
7896 expr = Expr::Like {
7897 expr: Box::new(expr),
7898 pattern: Box::new(pattern),
7899 negated,
7900 case_insensitive: false,
7901 };
7902 continue;
7903 }
7904 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("ilike")) {
7907 self.advance();
7908 let pattern = self.parse_expr(5)?;
7909 expr = Expr::Like {
7910 expr: Box::new(expr),
7911 pattern: Box::new(pattern),
7912 negated,
7913 case_insensitive: true,
7914 };
7915 continue;
7916 }
7917 if matches!(self.peek(), Token::LBracket) {
7921 self.advance();
7922 let index = self.parse_expr(0)?;
7923 if !matches!(self.peek(), Token::RBracket) {
7924 return Err(self.err(alloc::format!(
7925 "expected ']' after array index, got {:?}",
7926 self.peek()
7927 )));
7928 }
7929 self.advance();
7930 expr = Expr::ArraySubscript {
7931 target: Box::new(expr),
7932 index: Box::new(index),
7933 };
7934 continue;
7935 }
7936 return Ok(expr);
7937 }
7938 }
7939
7940 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7944 self.advance(); let low = self.parse_expr(5)?;
7946 if !matches!(self.peek(), Token::And) {
7947 return Err(self.err(format!(
7948 "expected AND after BETWEEN low bound, got {:?}",
7949 self.peek()
7950 )));
7951 }
7952 self.advance();
7953 let high = self.parse_expr(5)?;
7954 let target = Box::new(expr);
7955 let combined = Expr::Binary {
7956 lhs: Box::new(Expr::Binary {
7957 lhs: target.clone(),
7958 op: BinOp::GtEq,
7959 rhs: Box::new(low),
7960 }),
7961 op: BinOp::And,
7962 rhs: Box::new(Expr::Binary {
7963 lhs: target,
7964 op: BinOp::LtEq,
7965 rhs: Box::new(high),
7966 }),
7967 };
7968 Ok(maybe_not(combined, negated))
7969 }
7970
7971 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
7976 let mut recursive = false;
7981 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
7982 && s.eq_ignore_ascii_case("recursive")
7983 {
7984 self.advance();
7985 recursive = true;
7986 }
7987 let mut ctes = Vec::new();
7988 loop {
7989 let name = self.expect_ident_like()?;
7990 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
7994 self.advance();
7995 let mut names = Vec::new();
7996 loop {
7997 names.push(self.expect_ident_like()?);
7998 if matches!(self.peek(), Token::Comma) {
7999 self.advance();
8000 continue;
8001 }
8002 break;
8003 }
8004 if !matches!(self.peek(), Token::RParen) {
8005 return Err(self.err(format!(
8006 "expected ')' to close CTE column list, got {:?}",
8007 self.peek()
8008 )));
8009 }
8010 self.advance();
8011 names
8012 } else {
8013 Vec::new()
8014 };
8015 if !matches!(self.peek(), Token::As) {
8019 return Err(self.err(format!(
8020 "expected AS after CTE name {name:?}, got {:?}",
8021 self.peek()
8022 )));
8023 }
8024 self.advance();
8025 if !matches!(self.peek(), Token::LParen) {
8026 return Err(self.err(format!(
8027 "expected '(' after AS in WITH clause, got {:?}",
8028 self.peek()
8029 )));
8030 }
8031 self.advance();
8032 if !matches!(self.peek(), Token::Select) {
8033 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
8034 }
8035 let inner = self.parse_select_stmt()?;
8036 if !matches!(self.peek(), Token::RParen) {
8037 return Err(self.err(format!(
8038 "expected ')' after CTE body, got {:?}",
8039 self.peek()
8040 )));
8041 }
8042 self.advance();
8043 let Statement::Select(body) = inner else {
8044 unreachable!("parse_select_stmt returns Select")
8045 };
8046 ctes.push(crate::ast::Cte {
8047 name,
8048 body,
8049 recursive,
8050 column_overrides,
8051 });
8052 if matches!(self.peek(), Token::Comma) {
8053 self.advance();
8054 continue;
8055 }
8056 break;
8057 }
8058 if !matches!(self.peek(), Token::Select) {
8060 return Err(self.err(format!(
8061 "expected SELECT after WITH clause, got {:?}",
8062 self.peek()
8063 )));
8064 }
8065 let body_stmt = self.parse_select_stmt()?;
8066 let Statement::Select(mut body) = body_stmt else {
8067 unreachable!()
8068 };
8069 body.ctes = ctes;
8070 Ok(Statement::Select(body))
8071 }
8072
8073 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
8082 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
8086 None
8087 } else {
8088 Some(Box::new(self.parse_expr(0)?))
8089 };
8090 let mut branches: Vec<(Expr, Expr)> = Vec::new();
8091 loop {
8092 match self.peek() {
8093 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
8094 self.advance();
8095 let cond = self.parse_expr(0)?;
8096 match self.peek() {
8097 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
8098 self.advance();
8099 }
8100 other => {
8101 return Err(self.err(alloc::format!(
8102 "expected THEN after CASE WHEN <expr>, got {other:?}"
8103 )));
8104 }
8105 }
8106 let value = self.parse_expr(0)?;
8107 branches.push((cond, value));
8108 }
8109 _ => break,
8110 }
8111 }
8112 if branches.is_empty() {
8113 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
8114 }
8115 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
8116 {
8117 self.advance();
8118 Some(Box::new(self.parse_expr(0)?))
8119 } else {
8120 None
8121 };
8122 match self.peek() {
8123 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
8124 self.advance();
8125 }
8126 other => {
8127 return Err(self.err(alloc::format!(
8128 "expected END to close CASE expression, got {other:?}"
8129 )));
8130 }
8131 }
8132 Ok(Expr::Case {
8133 operand,
8134 branches,
8135 else_branch,
8136 })
8137 }
8138
8139 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
8140 if !matches!(self.peek(), Token::LParen) {
8141 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
8142 }
8143 self.advance();
8144 let inner = self.parse_select_stmt()?;
8145 if !matches!(self.peek(), Token::RParen) {
8146 return Err(self.err(format!(
8147 "expected ')' after EXISTS-subquery, got {:?}",
8148 self.peek()
8149 )));
8150 }
8151 self.advance();
8152 let Statement::Select(s) = inner else {
8153 unreachable!("parse_select_stmt returns Select")
8154 };
8155 Ok(Expr::Exists {
8156 subquery: Box::new(s),
8157 negated,
8158 })
8159 }
8160
8161 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8162 self.advance(); if !matches!(self.peek(), Token::LParen) {
8164 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
8165 }
8166 self.advance();
8167 if matches!(self.peek(), Token::Select) {
8169 let inner = self.parse_select_stmt()?;
8170 if !matches!(self.peek(), Token::RParen) {
8171 return Err(self.err(format!(
8172 "expected ')' after IN-subquery, got {:?}",
8173 self.peek()
8174 )));
8175 }
8176 self.advance();
8177 let Statement::Select(s) = inner else {
8178 unreachable!("parse_select_stmt always returns Statement::Select")
8179 };
8180 return Ok(Expr::InSubquery {
8181 expr: Box::new(expr),
8182 subquery: Box::new(s),
8183 negated,
8184 });
8185 }
8186 let mut elements = Vec::new();
8187 if !matches!(self.peek(), Token::RParen) {
8188 loop {
8189 elements.push(self.parse_expr(0)?);
8190 match self.peek() {
8191 Token::Comma => {
8192 self.advance();
8193 }
8194 Token::RParen => break,
8195 other => {
8196 return Err(
8197 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
8198 );
8199 }
8200 }
8201 }
8202 }
8203 self.advance(); let target = Box::new(expr);
8205 let combined = if elements.is_empty() {
8206 Expr::Literal(Literal::Bool(false))
8207 } else {
8208 let mut iter = elements.into_iter();
8209 let first = iter.next().unwrap();
8210 let mut acc = Expr::Binary {
8211 lhs: target.clone(),
8212 op: BinOp::Eq,
8213 rhs: Box::new(first),
8214 };
8215 for elt in iter {
8216 acc = Expr::Binary {
8217 lhs: Box::new(acc),
8218 op: BinOp::Or,
8219 rhs: Box::new(Expr::Binary {
8220 lhs: target.clone(),
8221 op: BinOp::Eq,
8222 rhs: Box::new(elt),
8223 }),
8224 };
8225 }
8226 acc
8227 };
8228 Ok(maybe_not(combined, negated))
8229 }
8230
8231 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
8252 if !matches!(self.peek(), Token::LParen) {
8255 return Err(self.err(alloc::format!(
8256 "expected '(' after MATCH, got {:?}",
8257 self.peek()
8258 )));
8259 }
8260 self.advance();
8261 let mut cols: Vec<Expr> = Vec::new();
8262 loop {
8263 cols.push(self.parse_expr(0)?);
8264 match self.peek() {
8265 Token::Comma => {
8266 self.advance();
8267 }
8268 Token::RParen => break,
8269 other => {
8270 return Err(self.err(alloc::format!(
8271 "expected ',' or ')' in MATCH column list, got {other:?}"
8272 )));
8273 }
8274 }
8275 }
8276 self.advance(); match self.peek() {
8279 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
8280 self.advance();
8281 }
8282 other => {
8283 return Err(self.err(alloc::format!(
8284 "expected AGAINST after MATCH column list, got {other:?}"
8285 )));
8286 }
8287 }
8288 if !matches!(self.peek(), Token::LParen) {
8289 return Err(self.err(alloc::format!(
8290 "expected '(' after AGAINST, got {:?}",
8291 self.peek()
8292 )));
8293 }
8294 self.advance();
8295 let term = match self.advance() {
8305 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
8306 Token::Placeholder(n) => Expr::Placeholder(n),
8307 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
8308 qualifier: None,
8309 name: s,
8310 }),
8311 other => {
8312 return Err(self.err(alloc::format!(
8313 "MATCH ... AGAINST(<term>) expects a string literal, \
8314 bound parameter, or column ref, got {other:?}"
8315 )));
8316 }
8317 };
8318 loop {
8323 match self.peek() {
8324 Token::In => {
8327 self.advance();
8328 }
8329 Token::Ident(s) | Token::QuotedIdent(s)
8330 if s.eq_ignore_ascii_case("natural")
8331 || s.eq_ignore_ascii_case("language")
8332 || s.eq_ignore_ascii_case("boolean")
8333 || s.eq_ignore_ascii_case("mode")
8334 || s.eq_ignore_ascii_case("with")
8335 || s.eq_ignore_ascii_case("query")
8336 || s.eq_ignore_ascii_case("expansion") =>
8337 {
8338 self.advance();
8339 }
8340 _ => break,
8341 }
8342 }
8343 if !matches!(self.peek(), Token::RParen) {
8344 return Err(self.err(alloc::format!(
8345 "expected ')' to close AGAINST, got {:?}",
8346 self.peek()
8347 )));
8348 }
8349 self.advance();
8350 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
8353 let plainto = Expr::FunctionCall {
8354 name: String::from("plainto_tsquery"),
8355 args: alloc::vec![simple_lit(), term.clone()],
8356 };
8357 let mut folded: Option<Expr> = None;
8358 for col in cols {
8359 let to_tsv = Expr::FunctionCall {
8360 name: String::from("to_tsvector"),
8361 args: alloc::vec![simple_lit(), col],
8362 };
8363 let leaf = Expr::Binary {
8364 lhs: Box::new(to_tsv),
8365 op: crate::ast::BinOp::TsMatch,
8366 rhs: Box::new(plainto.clone()),
8367 };
8368 folded = Some(match folded {
8369 None => leaf,
8370 Some(prev) => Expr::Binary {
8371 lhs: Box::new(prev),
8372 op: crate::ast::BinOp::Or,
8373 rhs: Box::new(leaf),
8374 },
8375 });
8376 }
8377 match folded {
8378 Some(e) => Ok(e),
8379 None => Err(self.err(String::from(
8380 "MATCH(...) AGAINST(...) requires at least one column",
8381 ))),
8382 }
8383 }
8384
8385 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
8386 if !matches!(self.peek(), Token::LParen) {
8387 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
8388 }
8389 self.advance();
8390 let field_name = self.expect_ident_like()?;
8391 let field = match field_name.to_ascii_lowercase().as_str() {
8392 "year" => ExtractField::Year,
8393 "month" => ExtractField::Month,
8394 "day" => ExtractField::Day,
8395 "hour" => ExtractField::Hour,
8396 "minute" => ExtractField::Minute,
8397 "second" => ExtractField::Second,
8398 "microsecond" | "microseconds" => ExtractField::Microsecond,
8399 "epoch" => ExtractField::Epoch,
8400 other => {
8401 return Err(self.err(format!(
8402 "unknown EXTRACT field {other:?}; \
8403 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
8404 )));
8405 }
8406 };
8407 if !matches!(self.peek(), Token::From) {
8408 return Err(self.err(format!(
8409 "expected FROM after EXTRACT field, got {:?}",
8410 self.peek()
8411 )));
8412 }
8413 self.advance();
8414 let source = self.parse_expr(0)?;
8415 if !matches!(self.peek(), Token::RParen) {
8416 return Err(self.err(format!(
8417 "expected ')' to close EXTRACT, got {:?}",
8418 self.peek()
8419 )));
8420 }
8421 self.advance();
8422 Ok(Expr::Extract {
8423 field,
8424 source: Box::new(source),
8425 })
8426 }
8427
8428 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8433 let tok = self.advance();
8434 let Token::String(text) = tok else {
8435 return Err(self.err(format!(
8436 "expected string literal after INTERVAL, got {tok:?}"
8437 )));
8438 };
8439 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8440 message: format!(
8441 "cannot parse INTERVAL {text:?}; \
8442 expected `<n> <unit> [<n> <unit> ...]` with units \
8443 microsecond[s], millisecond[s], second[s], minute[s], \
8444 hour[s], day[s], week[s], month[s], year[s]"
8445 ),
8446 token_pos: self.pos.saturating_sub(1),
8447 })?;
8448 Ok(Expr::Literal(Literal::Interval {
8449 months,
8450 micros,
8451 text,
8452 }))
8453 }
8454
8455 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8456 let mut elems = Vec::new();
8457 if matches!(self.peek(), Token::RBracket) {
8458 self.advance();
8459 return Ok(Expr::Literal(Literal::Vector(elems)));
8460 }
8461 loop {
8462 let e = self.parse_expr(0)?;
8463 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8464 message: format!("vector element must be a numeric literal, got {e:?}"),
8465 token_pos: self.pos,
8466 })?;
8467 elems.push(x);
8468 match self.peek() {
8469 Token::Comma => {
8470 self.advance();
8471 }
8472 Token::RBracket => {
8473 self.advance();
8474 break;
8475 }
8476 other => {
8477 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8478 }
8479 }
8480 }
8481 Ok(Expr::Literal(Literal::Vector(elems)))
8482 }
8483
8484 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8493 let Token::Ident(s) = self.peek().clone() else {
8494 return NullTreatment::Respect;
8495 };
8496 let is_ignore = s.eq_ignore_ascii_case("ignore");
8497 let is_respect = s.eq_ignore_ascii_case("respect");
8498 if !is_ignore && !is_respect {
8499 return NullTreatment::Respect;
8500 }
8501 if self.pos + 1 < self.tokens.len()
8504 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8505 && s2.eq_ignore_ascii_case("nulls")
8506 {
8507 self.advance();
8508 self.advance();
8509 return if is_ignore {
8510 NullTreatment::Ignore
8511 } else {
8512 NullTreatment::Respect
8513 };
8514 }
8515 NullTreatment::Respect
8516 }
8517
8518 #[allow(clippy::type_complexity)] fn parse_over_clause(
8521 &mut self,
8522 ) -> Result<
8523 (
8524 Vec<Expr>,
8525 Vec<(Expr, bool, Option<bool>)>,
8526 Option<WindowFrame>,
8527 ),
8528 ParseError,
8529 > {
8530 if !matches!(self.peek(), Token::LParen) {
8531 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8532 }
8533 self.advance();
8534 let mut partition_by = Vec::new();
8535 let mut order_by = Vec::new();
8536 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8538 && s.eq_ignore_ascii_case("partition")
8539 {
8540 self.advance();
8541 if !matches!(self.peek(), Token::By) {
8542 return Err(self.err(format!(
8543 "expected BY after PARTITION, got {:?}",
8544 self.peek()
8545 )));
8546 }
8547 self.advance();
8548 loop {
8549 partition_by.push(self.parse_expr(0)?);
8550 if matches!(self.peek(), Token::Comma) {
8551 self.advance();
8552 continue;
8553 }
8554 break;
8555 }
8556 }
8557 if matches!(self.peek(), Token::Order) {
8559 self.advance();
8560 if !matches!(self.peek(), Token::By) {
8561 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8562 }
8563 self.advance();
8564 loop {
8565 let e = self.parse_expr(0)?;
8566 let desc = if matches!(self.peek(), Token::Desc) {
8567 self.advance();
8568 true
8569 } else if matches!(self.peek(), Token::Asc) {
8570 self.advance();
8571 false
8572 } else {
8573 false
8574 };
8575 let nulls_first = self.parse_optional_nulls_placement()?;
8577 order_by.push((e, desc, nulls_first));
8578 if matches!(self.peek(), Token::Comma) {
8579 self.advance();
8580 continue;
8581 }
8582 break;
8583 }
8584 }
8585 let mut frame: Option<WindowFrame> = None;
8589 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8590 let kind = if s.eq_ignore_ascii_case("rows") {
8591 Some(FrameKind::Rows)
8592 } else if s.eq_ignore_ascii_case("range") {
8593 Some(FrameKind::Range)
8594 } else {
8595 None
8596 };
8597 if let Some(kind) = kind {
8598 self.advance();
8599 frame = Some(self.parse_frame_tail(kind)?);
8600 }
8601 }
8602 if !matches!(self.peek(), Token::RParen) {
8603 return Err(self.err(format!(
8604 "expected ')' to close OVER clause, got {:?}",
8605 self.peek()
8606 )));
8607 }
8608 self.advance();
8609 Ok((partition_by, order_by, frame))
8610 }
8611
8612 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8618 if matches!(self.peek(), Token::Between) {
8619 self.advance();
8620 let start = self.parse_frame_bound()?;
8621 if !matches!(self.peek(), Token::And) {
8622 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8623 }
8624 self.advance();
8625 let end = self.parse_frame_bound()?;
8626 Ok(WindowFrame {
8627 kind,
8628 start,
8629 end: Some(end),
8630 })
8631 } else {
8632 let start = self.parse_frame_bound()?;
8633 Ok(WindowFrame {
8634 kind,
8635 start,
8636 end: None,
8637 })
8638 }
8639 }
8640
8641 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8644 if let Token::Integer(n) = *self.peek() {
8646 self.advance();
8647 let n: u64 = u64::try_from(n).map_err(|_| {
8648 self.err(format!(
8649 "invalid frame offset {n} — expected non-negative integer"
8650 ))
8651 })?;
8652 let dir = self.expect_ident_like()?;
8653 return if dir.eq_ignore_ascii_case("preceding") {
8654 Ok(FrameBound::OffsetPreceding(n))
8655 } else if dir.eq_ignore_ascii_case("following") {
8656 Ok(FrameBound::OffsetFollowing(n))
8657 } else {
8658 Err(self.err(format!(
8659 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8660 )))
8661 };
8662 }
8663 let first = self.expect_ident_like()?;
8664 if first.eq_ignore_ascii_case("unbounded") {
8665 let dir = self.expect_ident_like()?;
8666 return if dir.eq_ignore_ascii_case("preceding") {
8667 Ok(FrameBound::UnboundedPreceding)
8668 } else if dir.eq_ignore_ascii_case("following") {
8669 Ok(FrameBound::UnboundedFollowing)
8670 } else {
8671 Err(self.err(format!(
8672 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8673 )))
8674 };
8675 }
8676 if first.eq_ignore_ascii_case("current") {
8677 let row = self.expect_ident_like()?;
8678 if !row.eq_ignore_ascii_case("row") {
8679 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8680 }
8681 return Ok(FrameBound::CurrentRow);
8682 }
8683 Err(self.err(format!(
8684 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8685 )))
8686 }
8687
8688 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8689 if matches!(self.peek(), Token::Dot) {
8690 self.advance();
8691 let name = self.expect_ident_like()?;
8692 if matches!(self.peek(), Token::LParen) {
8698 return self.finish_ident_atom(name);
8699 }
8700 return Ok(Expr::Column(ColumnName {
8701 qualifier: Some(first),
8702 name,
8703 }));
8704 }
8705 if matches!(self.peek(), Token::LParen) {
8706 self.advance();
8707 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8711 self.advance();
8712 if !matches!(self.peek(), Token::RParen) {
8713 return Err(self.err(format!(
8714 "expected ')' after COUNT(*), got {:?}",
8715 self.peek()
8716 )));
8717 }
8718 self.advance();
8719 let null_treatment = self.parse_null_treatment_modifier();
8721 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8722 && s.eq_ignore_ascii_case("over")
8723 {
8724 self.advance();
8725 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8726 return Ok(Expr::WindowFunction {
8727 name: "count_star".into(),
8728 args: Vec::new(),
8729 partition_by,
8730 order_by,
8731 frame,
8732 null_treatment,
8733 });
8734 }
8735 return Ok(Expr::FunctionCall {
8736 name: "count_star".into(),
8737 args: Vec::new(),
8738 });
8739 }
8740 let mut args = Vec::new();
8742 let mut agg_order_by: Vec<OrderBy> = Vec::new();
8743 let agg_distinct = if matches!(self.peek(), Token::Distinct) {
8745 self.advance();
8746 true
8747 } else {
8748 false
8749 };
8750 if !matches!(self.peek(), Token::RParen) {
8751 loop {
8752 args.push(self.parse_expr(0)?);
8753 if first.eq_ignore_ascii_case("cast")
8757 && args.len() == 1
8758 && matches!(self.peek(), Token::As)
8759 {
8760 self.advance();
8761 let target = self.parse_cast_target()?;
8762 if !matches!(self.peek(), Token::RParen) {
8763 return Err(self.err(format!(
8764 "expected ')' to close CAST, got {:?}",
8765 self.peek()
8766 )));
8767 }
8768 self.advance();
8769 return Ok(Expr::Cast {
8770 expr: Box::new(args.pop().expect("one arg")),
8771 target,
8772 });
8773 }
8774 if matches!(self.peek(), Token::Order) {
8778 self.advance();
8779 if !matches!(self.peek(), Token::By) {
8780 return Err(self.err(format!(
8781 "expected BY after ORDER in aggregate args, got {:?}",
8782 self.peek()
8783 )));
8784 }
8785 self.advance();
8786 loop {
8787 let expr = self.parse_expr(0)?;
8788 let desc = if matches!(self.peek(), Token::Desc) {
8789 self.advance();
8790 true
8791 } else if matches!(self.peek(), Token::Asc) {
8792 self.advance();
8793 false
8794 } else {
8795 false
8796 };
8797 let nulls_first = self.parse_optional_nulls_placement()?;
8798 agg_order_by.push(OrderBy {
8799 expr,
8800 desc,
8801 nulls_first,
8802 });
8803 if matches!(self.peek(), Token::Comma) {
8804 self.advance();
8805 } else {
8806 break;
8807 }
8808 }
8809 if !matches!(self.peek(), Token::RParen) {
8810 return Err(self.err(format!(
8811 "expected ')' after aggregate ORDER BY, got {:?}",
8812 self.peek()
8813 )));
8814 }
8815 break;
8816 }
8817 match self.peek() {
8818 Token::Comma => {
8819 self.advance();
8820 }
8821 Token::RParen => break,
8822 other => {
8823 return Err(self.err(format!(
8824 "expected ',' or ')' in function args, got {other:?}"
8825 )));
8826 }
8827 }
8828 }
8829 }
8830 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8838 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8839 && s.eq_ignore_ascii_case("over")
8840 {
8841 self.advance();
8842 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8843 return Ok(Expr::WindowFunction {
8844 name: first,
8845 args,
8846 partition_by,
8847 order_by,
8848 frame,
8849 null_treatment,
8850 });
8851 }
8852 if !agg_order_by.is_empty() || agg_distinct {
8853 return Ok(Expr::AggregateOrdered {
8854 call: Box::new(Expr::FunctionCall { name: first, args }),
8855 order_by: agg_order_by,
8856 distinct: agg_distinct,
8857 });
8858 }
8859 return Ok(Expr::FunctionCall { name: first, args });
8860 }
8861 let lc = first.to_ascii_lowercase();
8867 if matches!(
8868 lc.as_str(),
8869 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8870 ) {
8871 return Ok(Expr::FunctionCall {
8872 name: lc,
8873 args: Vec::new(),
8874 });
8875 }
8876 Ok(Expr::Column(ColumnName {
8877 qualifier: None,
8878 name: first,
8879 }))
8880 }
8881}
8882
8883fn extract_first_column(expr: &Expr) -> Option<String> {
8891 match expr {
8892 Expr::Column(cn) => Some(cn.name.clone()),
8893 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8894 Expr::Binary { lhs, rhs, .. } => {
8895 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8896 }
8897 Expr::Unary { expr: e, .. } => extract_first_column(e),
8898 _ => None,
8899 }
8900}
8901
8902fn maybe_not(expr: Expr, negated: bool) -> Expr {
8903 if negated {
8904 Expr::Unary {
8905 op: UnOp::Not,
8906 expr: Box::new(expr),
8907 }
8908 } else {
8909 expr
8910 }
8911}
8912
8913fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8914 let pair = match tok {
8915 Token::Or => (BinOp::Or, 1),
8916 Token::And => (BinOp::And, 2),
8917 Token::Eq => (BinOp::Eq, 4),
8918 Token::NotEq => (BinOp::NotEq, 4),
8919 Token::Lt => (BinOp::Lt, 4),
8920 Token::LtEq => (BinOp::LtEq, 4),
8921 Token::Gt => (BinOp::Gt, 4),
8922 Token::GtEq => (BinOp::GtEq, 4),
8923 Token::L2Distance => (BinOp::L2Distance, 5),
8926 Token::InnerProduct => (BinOp::InnerProduct, 5),
8927 Token::CosineDistance => (BinOp::CosineDistance, 5),
8928 Token::Plus => (BinOp::Add, 6),
8929 Token::Minus => (BinOp::Sub, 6),
8930 Token::Concat => (BinOp::Concat, 6),
8933 Token::Pipe => (BinOp::BitOr, 6),
8946 Token::Amp => (BinOp::BitAnd, 6),
8947 Token::Star => (BinOp::Mul, 7),
8948 Token::Slash => (BinOp::Div, 7),
8949 Token::JsonGet => (BinOp::JsonGet, 7),
8953 Token::JsonGetText => (BinOp::JsonGetText, 7),
8954 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
8955 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
8956 Token::JsonContains => (BinOp::JsonContains, 7),
8957 Token::TsMatch => (BinOp::TsMatch, 4),
8961 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
8965 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
8966 Token::InetContains => (BinOp::InetContains, 4),
8967 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
8968 Token::InetOverlap => (BinOp::InetOverlap, 4),
8969 _ => return None,
8970 };
8971 Some(pair)
8972}
8973
8974#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
8975fn is_alias_stopword(s: &str) -> bool {
8987 matches!(
8988 s.to_ascii_lowercase().as_str(),
8989 "with"
8990 | "on"
8991 | "where"
8992 | "having"
8993 | "group"
8994 | "order"
8995 | "limit"
8996 | "offset"
8997 | "union"
8998 | "except"
8999 | "intersect"
9000 | "returning"
9001 | "set"
9002 | "values"
9003 | "for"
9004 | "lateral"
9005 | "left"
9006 | "right"
9007 | "inner"
9008 | "outer"
9009 | "full"
9010 | "cross"
9011 | "join"
9012 | "natural"
9013 | "using"
9014 | "fetch"
9015 )
9016}
9017
9018fn extract_numeric_literal(e: &Expr) -> Option<f32> {
9019 match e {
9020 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
9021 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
9022 Expr::Unary {
9023 op: UnOp::Neg,
9024 expr,
9025 } => extract_numeric_literal(expr).map(|x| -x),
9026 _ => None,
9027 }
9028}
9029
9030pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
9038 let parts: Vec<&str> = s.split_whitespace().collect();
9039 if parts.is_empty() || !parts.len().is_multiple_of(2) {
9040 return None;
9041 }
9042 let mut months: i32 = 0;
9043 let mut micros: i64 = 0;
9044 let mut i = 0;
9045 while i < parts.len() {
9046 let n: i64 = parts[i].parse().ok()?;
9047 let unit = parts[i + 1].to_ascii_lowercase();
9048 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
9049 match unit_stripped {
9050 "microsecond" => micros = micros.checked_add(n)?,
9051 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
9052 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
9053 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
9054 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
9055 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
9056 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
9057 "month" => {
9058 let n32 = i32::try_from(n).ok()?;
9059 months = months.checked_add(n32)?;
9060 }
9061 "year" => {
9062 let n32 = i32::try_from(n).ok()?;
9063 months = months.checked_add(n32.checked_mul(12)?)?;
9064 }
9065 _ => return None,
9066 }
9067 i += 2;
9068 }
9069 Some((months, micros))
9070}
9071
9072fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
9083 Some(match ident.to_ascii_lowercase().as_str() {
9084 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
9085 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
9086 "bigint" => ColumnTypeName::BigInt,
9087 "float" | "double" | "real" => ColumnTypeName::Float,
9088 "text" => ColumnTypeName::Text,
9089 "bool" | "boolean" => ColumnTypeName::Bool,
9090 "date" => ColumnTypeName::Date,
9091 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
9092 "timestamptz" => ColumnTypeName::Timestamptz,
9093 "json" => ColumnTypeName::Json,
9094 "jsonb" => ColumnTypeName::Jsonb,
9095 "bytea" | "bytes" => ColumnTypeName::Bytes,
9096 "tsvector" => ColumnTypeName::TsVector,
9097 "tsquery" => ColumnTypeName::TsQuery,
9098 "uuid" => ColumnTypeName::Uuid,
9099 "time" => ColumnTypeName::Time,
9100 "year" => ColumnTypeName::Year,
9101 "timetz" => ColumnTypeName::TimeTz,
9102 "money" => ColumnTypeName::Money,
9103 _ => return None,
9104 })
9105}
9106
9107pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9134 parse_plpgsql_body(body)
9135}
9136
9137fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9138 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
9142 message: alloc::format!("plpgsql body lex error: {e}"),
9143 token_pos: 0,
9144 })?;
9145 let mut parser = Parser::new(tokens);
9146 parser.parse_plpgsql_block()
9147}
9148
9149#[cfg(test)]
9150mod tests {
9151 use super::*;
9152 use alloc::string::ToString;
9153
9154 fn parse(s: &str) -> Statement {
9155 parse_statement(s).expect("parse ok")
9156 }
9157
9158 fn lit_int(n: i64) -> Expr {
9159 Expr::Literal(Literal::Integer(n))
9160 }
9161
9162 fn col(name: &str) -> Expr {
9163 Expr::Column(ColumnName {
9164 qualifier: None,
9165 name: name.into(),
9166 })
9167 }
9168
9169 #[test]
9170 fn select_single_integer() {
9171 let s = parse("SELECT 1");
9172 let Statement::Select(s) = s else {
9173 panic!("expected SELECT")
9174 };
9175 assert_eq!(s.items.len(), 1);
9176 assert!(s.from.is_none());
9177 assert!(s.where_.is_none());
9178 }
9179
9180 #[test]
9181 fn select_multiple_literal_kinds() {
9182 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
9183 let Statement::Select(s) = s else {
9184 panic!("expected SELECT")
9185 };
9186 assert_eq!(s.items.len(), 5);
9187 }
9188
9189 #[test]
9190 fn select_wildcard_from_table() {
9191 let s = parse("SELECT * FROM users");
9192 let Statement::Select(s) = s else {
9193 panic!("expected SELECT")
9194 };
9195 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
9196 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
9197 }
9198
9199 #[test]
9200 fn select_with_table_alias() {
9201 let s = parse("SELECT * FROM users AS u");
9202 let Statement::Select(s) = s else {
9203 panic!("expected SELECT")
9204 };
9205 let t = &s.from.as_ref().unwrap().primary;
9206 assert_eq!(t.name, "users");
9207 assert_eq!(t.alias.as_deref(), Some("u"));
9208 }
9209
9210 #[test]
9211 fn select_with_where_eq() {
9212 let s = parse("SELECT a FROM t WHERE a = 1");
9213 let Statement::Select(s) = s else {
9214 panic!("expected SELECT")
9215 };
9216 let w = s.where_.unwrap();
9217 assert_eq!(
9218 w,
9219 Expr::Binary {
9220 lhs: Box::new(col("a")),
9221 op: BinOp::Eq,
9222 rhs: Box::new(lit_int(1)),
9223 }
9224 );
9225 }
9226
9227 #[test]
9228 fn arithmetic_precedence() {
9229 let s = parse("SELECT 1 + 2 * 3");
9230 let Statement::Select(s) = s else {
9231 panic!("expected SELECT")
9232 };
9233 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9234 panic!("wildcard?")
9235 };
9236 assert_eq!(
9237 expr,
9238 &Expr::Binary {
9239 lhs: Box::new(lit_int(1)),
9240 op: BinOp::Add,
9241 rhs: Box::new(Expr::Binary {
9242 lhs: Box::new(lit_int(2)),
9243 op: BinOp::Mul,
9244 rhs: Box::new(lit_int(3)),
9245 }),
9246 }
9247 );
9248 }
9249
9250 #[test]
9251 fn parentheses_override_precedence() {
9252 let s = parse("SELECT (1 + 2) * 3");
9253 let Statement::Select(s) = s else {
9254 panic!("expected SELECT")
9255 };
9256 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9257 panic!()
9258 };
9259 assert_eq!(
9260 expr,
9261 &Expr::Binary {
9262 lhs: Box::new(Expr::Binary {
9263 lhs: Box::new(lit_int(1)),
9264 op: BinOp::Add,
9265 rhs: Box::new(lit_int(2)),
9266 }),
9267 op: BinOp::Mul,
9268 rhs: Box::new(lit_int(3)),
9269 }
9270 );
9271 }
9272
9273 #[test]
9274 fn not_binds_below_comparison() {
9275 let s = parse("SELECT NOT a = 1 FROM t");
9277 let Statement::Select(s) = s else {
9278 panic!("expected SELECT")
9279 };
9280 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9281 panic!()
9282 };
9283 assert_eq!(
9284 expr,
9285 &Expr::Unary {
9286 op: UnOp::Not,
9287 expr: Box::new(Expr::Binary {
9288 lhs: Box::new(col("a")),
9289 op: BinOp::Eq,
9290 rhs: Box::new(lit_int(1)),
9291 }),
9292 }
9293 );
9294 }
9295
9296 #[test]
9297 fn unary_minus_binds_above_multiplication() {
9298 let s = parse("SELECT -a * 2 FROM t");
9300 let Statement::Select(s) = s else {
9301 panic!("expected SELECT")
9302 };
9303 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9304 panic!()
9305 };
9306 assert_eq!(
9307 expr,
9308 &Expr::Binary {
9309 lhs: Box::new(Expr::Unary {
9310 op: UnOp::Neg,
9311 expr: Box::new(col("a")),
9312 }),
9313 op: BinOp::Mul,
9314 rhs: Box::new(lit_int(2)),
9315 }
9316 );
9317 }
9318
9319 #[test]
9320 fn qualified_column() {
9321 let s = parse("SELECT t.col FROM t");
9322 let Statement::Select(s) = s else {
9323 panic!("expected SELECT")
9324 };
9325 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9326 panic!()
9327 };
9328 assert_eq!(
9329 expr,
9330 &Expr::Column(ColumnName {
9331 qualifier: Some("t".into()),
9332 name: "col".into()
9333 })
9334 );
9335 }
9336
9337 #[test]
9338 fn select_item_alias_with_as() {
9339 let s = parse("SELECT a AS y FROM t");
9340 let Statement::Select(s) = s else {
9341 panic!("expected SELECT")
9342 };
9343 let SelectItem::Expr { alias, .. } = &s.items[0] else {
9344 panic!()
9345 };
9346 assert_eq!(alias.as_deref(), Some("y"));
9347 }
9348
9349 #[test]
9350 fn trailing_semicolon_accepted() {
9351 let s = parse("SELECT 1;");
9352 let Statement::Select(s) = s else {
9353 panic!("expected SELECT")
9354 };
9355 assert_eq!(s.items.len(), 1);
9356 }
9357
9358 #[test]
9359 fn boolean_chain_with_and_or_not() {
9360 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
9362 let Statement::Select(s) = s else {
9363 panic!("expected SELECT")
9364 };
9365 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9366 panic!()
9367 };
9368 let expected = Expr::Binary {
9369 lhs: Box::new(Expr::Unary {
9370 op: UnOp::Not,
9371 expr: Box::new(col("a")),
9372 }),
9373 op: BinOp::Or,
9374 rhs: Box::new(Expr::Binary {
9375 lhs: Box::new(col("b")),
9376 op: BinOp::And,
9377 rhs: Box::new(Expr::Unary {
9378 op: UnOp::Not,
9379 expr: Box::new(col("c")),
9380 }),
9381 }),
9382 };
9383 assert_eq!(expr, &expected);
9384 }
9385
9386 #[test]
9387 fn empty_input_errors() {
9388 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
9395 assert!(matches!(
9396 parse_statement(" \n\t ").unwrap(),
9397 Statement::Empty
9398 ));
9399 assert!(parse_statement("SELECT FROM WHERE").is_err());
9401 }
9402
9403 #[test]
9404 fn unmatched_paren_errors() {
9405 assert!(parse_statement("SELECT (1 + 2").is_err());
9406 }
9407
9408 #[test]
9409 fn display_round_trip_simple_select() {
9410 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
9411 let text = original.to_string();
9412 let again = parse_statement(&text).expect("re-parse");
9413 assert_eq!(original, again);
9414 }
9415
9416 #[test]
9419 fn create_table_single_column() {
9420 let s = parse("CREATE TABLE foo (a INT)");
9421 let Statement::CreateTable(c) = s else {
9422 panic!("expected CreateTable")
9423 };
9424 assert_eq!(c.name, "foo");
9425 assert_eq!(c.columns.len(), 1);
9426 assert_eq!(c.columns[0].name, "a");
9427 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9428 assert!(c.columns[0].nullable);
9429 }
9430
9431 #[test]
9432 fn create_table_multi_column_with_not_null_mix() {
9433 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
9434 let Statement::CreateTable(c) = s else {
9435 panic!()
9436 };
9437 assert_eq!(c.columns.len(), 4);
9438 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9439 assert!(!c.columns[0].nullable);
9440 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
9441 assert!(c.columns[1].nullable);
9442 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
9443 assert!(!c.columns[2].nullable);
9444 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
9445 }
9446
9447 #[test]
9448 fn create_table_bigint_supported() {
9449 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
9450 let Statement::CreateTable(c) = s else {
9451 panic!()
9452 };
9453 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
9454 }
9455
9456 #[test]
9457 fn create_table_vector_default_is_f32() {
9458 let s = parse("CREATE TABLE t (v VECTOR(128))");
9459 let Statement::CreateTable(c) = s else {
9460 panic!()
9461 };
9462 assert_eq!(
9463 c.columns[0].ty,
9464 ColumnTypeName::Vector {
9465 dim: 128,
9466 encoding: VecEncoding::F32,
9467 },
9468 );
9469 }
9470
9471 #[test]
9472 fn create_table_vector_using_sq8() {
9473 for sql in [
9476 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
9477 "CREATE TABLE t (v VECTOR(128) using sq8)",
9478 ] {
9479 let s = parse(sql);
9480 let Statement::CreateTable(c) = s else {
9481 panic!()
9482 };
9483 assert_eq!(
9484 c.columns[0].ty,
9485 ColumnTypeName::Vector {
9486 dim: 128,
9487 encoding: VecEncoding::Sq8,
9488 },
9489 "{sql}",
9490 );
9491 }
9492 }
9493
9494 #[test]
9495 fn create_table_vector_using_unknown_errors() {
9496 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
9505 assert!(
9506 err.message.contains("USING")
9507 || err.message.contains("using")
9508 || err.message.contains("')'")
9509 || err.message.contains("','"),
9510 "expected USING/column-list rejection, got: {}",
9511 err.message
9512 );
9513 }
9514
9515 #[test]
9516 fn vector_using_sq8_display_roundtrips() {
9517 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
9520 let Statement::CreateTable(c) = s else {
9521 panic!()
9522 };
9523 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9524 }
9525
9526 #[test]
9527 fn parser_recognises_placeholders() {
9528 use crate::ast::{Expr, SelectItem, Statement};
9529 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9531 let Statement::Select(sel) = s else { panic!() };
9532 assert!(matches!(
9533 sel.items[0],
9534 SelectItem::Expr {
9535 expr: Expr::Placeholder(1),
9536 alias: None
9537 }
9538 ));
9539 let SelectItem::Expr {
9541 expr: Expr::Binary { lhs, rhs, .. },
9542 ..
9543 } = &sel.items[1]
9544 else {
9545 panic!()
9546 };
9547 assert!(matches!(**lhs, Expr::Placeholder(2)));
9548 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9549 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9551 panic!()
9552 };
9553 assert!(matches!(**rhs, Expr::Placeholder(3)));
9554 }
9555
9556 #[test]
9557 fn parser_rejects_dollar_zero() {
9558 assert!(parse_statement("SELECT $0").is_err());
9560 }
9561
9562 #[test]
9563 fn placeholder_display_roundtrips() {
9564 let s = parse("SELECT $42 FROM t");
9567 let printed = s.to_string();
9568 assert!(printed.contains("$42"));
9569 let again = parse(&printed);
9570 assert_eq!(s, again);
9571 }
9572
9573 #[test]
9574 fn alter_index_rebuild_bare() {
9575 use crate::ast::{AlterIndexTarget, Statement};
9576 let s = parse("ALTER INDEX my_idx REBUILD");
9577 let Statement::AlterIndex(a) = s else {
9578 panic!("expected AlterIndex, got {s:?}")
9579 };
9580 assert_eq!(a.name, "my_idx");
9581 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9582 }
9583
9584 #[test]
9585 fn alter_index_rebuild_with_encoding() {
9586 use crate::ast::{AlterIndexTarget, Statement};
9587 for (sql, want) in [
9588 (
9589 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9590 VecEncoding::F32,
9591 ),
9592 (
9593 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9594 VecEncoding::Sq8,
9595 ),
9596 (
9597 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9598 VecEncoding::F16,
9599 ),
9600 ] {
9601 let s = parse(sql);
9602 let Statement::AlterIndex(a) = s else {
9603 panic!("{sql}: expected AlterIndex")
9604 };
9605 assert_eq!(a.name, "my_idx");
9606 assert_eq!(
9607 a.target,
9608 AlterIndexTarget::Rebuild {
9609 encoding: Some(want)
9610 },
9611 "{sql}"
9612 );
9613 }
9614 }
9615
9616 #[test]
9617 fn alter_index_rebuild_unknown_encoding_errors() {
9618 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9619 assert!(
9620 err.message.contains("unknown vector encoding"),
9621 "got: {}",
9622 err.message
9623 );
9624 }
9625
9626 #[test]
9627 fn alter_index_rebuild_display_roundtrips() {
9628 for (input, want) in [
9629 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9630 (
9631 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9632 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9633 ),
9634 (
9635 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9636 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9637 ),
9638 ] {
9639 let s = parse(input);
9640 assert_eq!(s.to_string(), want);
9641 }
9642 }
9643
9644 #[test]
9645 fn create_table_unknown_type_defers_to_engine() {
9646 let stmt = parse_statement("CREATE TABLE x (a xml)").unwrap();
9653 let Statement::CreateTable(t) = stmt else {
9654 panic!("expected CreateTable");
9655 };
9656 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("xml"));
9657 }
9658
9659 #[test]
9660 fn create_table_missing_table_keyword_errors() {
9661 assert!(parse_statement("CREATE x (a INT)").is_err());
9662 }
9663
9664 #[test]
9665 fn insert_single_value() {
9666 let s = parse("INSERT INTO foo VALUES (42)");
9667 let Statement::Insert(i) = s else {
9668 panic!("expected Insert")
9669 };
9670 assert_eq!(i.table, "foo");
9671 assert_eq!(i.rows.len(), 1);
9672 assert_eq!(i.rows[0].len(), 1);
9673 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9674 }
9675
9676 #[test]
9677 fn insert_multi_value_with_mixed_literals() {
9678 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9679 let Statement::Insert(i) = s else { panic!() };
9680 assert_eq!(i.rows.len(), 1);
9681 assert_eq!(i.rows[0].len(), 5);
9682 }
9683
9684 #[test]
9685 fn insert_missing_into_errors() {
9686 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9687 }
9688
9689 #[test]
9690 fn create_table_round_trip() {
9691 let original =
9692 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9693 let text = original.to_string();
9694 let again = parse_statement(&text).expect("re-parse");
9695 assert_eq!(original, again);
9696 }
9697
9698 #[test]
9699 fn insert_round_trip_with_negation_and_string() {
9700 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9701 let text = original.to_string();
9702 let again = parse_statement(&text).expect("re-parse");
9703 assert_eq!(original, again);
9704 }
9705
9706 #[test]
9707 fn unknown_keyword_at_statement_start_errors() {
9708 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9711 assert!(err.message.contains("expected SELECT"));
9712 }
9713
9714 #[test]
9717 fn create_index_basic() {
9718 let s = parse("CREATE INDEX idx_id ON users (id)");
9719 let Statement::CreateIndex(c) = s else {
9720 panic!("expected CreateIndex")
9721 };
9722 assert_eq!(c.name, "idx_id");
9723 assert_eq!(c.table, "users");
9724 assert_eq!(c.column, "id");
9725 }
9726
9727 #[test]
9728 fn create_index_missing_on_errors() {
9729 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9730 }
9731
9732 #[test]
9733 fn create_index_missing_paren_errors() {
9734 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9735 }
9736
9737 #[test]
9738 fn create_index_round_trip() {
9739 let original = parse("CREATE INDEX by_name ON users (name)");
9740 let again = parse_statement(&original.to_string()).unwrap();
9741 assert_eq!(original, again);
9742 }
9743
9744 #[test]
9747 fn create_unique_index_basic() {
9748 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9749 let Statement::CreateIndex(c) = s else {
9750 panic!("expected CreateIndex");
9751 };
9752 assert!(c.is_unique);
9753 assert_eq!(c.column, "a");
9754 assert!(c.partial_predicate.is_none());
9755 }
9756
9757 #[test]
9758 fn create_unique_index_partial() {
9759 let s = parse(
9761 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9762 ON email_templates (user_address) WHERE is_default = true",
9763 );
9764 let Statement::CreateIndex(c) = s else {
9765 panic!("expected CreateIndex");
9766 };
9767 assert!(c.is_unique);
9768 assert_eq!(c.table, "email_templates");
9769 assert_eq!(c.column, "user_address");
9770 assert!(c.partial_predicate.is_some());
9771 }
9772
9773 #[test]
9774 fn create_unique_index_composite_with_predicate() {
9775 let s = parse(
9777 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9778 ON calendar_events (calendar_id, uid, recurrence_id) \
9779 WHERE recurrence_id IS NOT NULL",
9780 );
9781 let Statement::CreateIndex(c) = s else {
9782 panic!("expected CreateIndex");
9783 };
9784 assert!(c.is_unique);
9785 assert_eq!(c.column, "calendar_id");
9786 assert_eq!(
9787 c.extra_columns,
9788 vec!["uid".to_string(), "recurrence_id".to_string()]
9789 );
9790 assert!(c.partial_predicate.is_some());
9791 }
9792
9793 #[test]
9794 fn create_unique_index_using_btree_ok() {
9795 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9796 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9797 }
9798
9799 #[test]
9800 fn create_unique_index_using_hnsw_rejected() {
9801 let err =
9802 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9803 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9804 }
9805
9806 #[test]
9807 fn create_unique_index_round_trip() {
9808 let original = parse(
9809 "CREATE UNIQUE INDEX uq_calendar_events_master \
9810 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9811 );
9812 let again = parse_statement(&original.to_string()).unwrap();
9813 assert_eq!(original, again);
9814 }
9815
9816 #[test]
9817 fn create_unique_without_index_errors() {
9818 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9819 assert!(err.message.contains("INDEX"), "{}", err.message);
9820 }
9821
9822 #[test]
9825 fn create_table_bytea_column() {
9826 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9827 let Statement::CreateTable(c) = s else {
9828 panic!("expected CreateTable");
9829 };
9830 assert_eq!(c.columns.len(), 2);
9831 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9832 assert!(!c.columns[1].nullable);
9833 }
9834
9835 #[test]
9836 fn create_table_bytes_alias_column() {
9837 let s = parse("CREATE TABLE t (blob BYTES)");
9838 let Statement::CreateTable(c) = s else {
9839 panic!("expected CreateTable");
9840 };
9841 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9842 }
9843
9844 #[test]
9845 fn bytea_round_trip_display() {
9846 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9847 let again = parse_statement(&original.to_string()).unwrap();
9848 assert_eq!(original, again);
9849 }
9850
9851 #[test]
9854 fn begin_commit_rollback_parse_as_unit_variants() {
9855 assert_eq!(parse("BEGIN"), Statement::Begin);
9856 assert_eq!(parse("COMMIT"), Statement::Commit);
9857 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9858 assert_eq!(parse("BEGIN;"), Statement::Begin);
9860 }
9861
9862 #[test]
9865 fn inner_product_binop_parses() {
9866 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9867 let Statement::Select(s) = s else { panic!() };
9868 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9869 panic!()
9870 };
9871 assert!(matches!(
9872 expr,
9873 Expr::Binary {
9874 op: BinOp::InnerProduct,
9875 ..
9876 }
9877 ));
9878 }
9879
9880 #[test]
9881 fn cosine_distance_binop_parses() {
9882 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9883 let Statement::Select(s) = s else { panic!() };
9884 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9885 panic!()
9886 };
9887 assert!(matches!(
9888 expr,
9889 Expr::Binary {
9890 op: BinOp::CosineDistance,
9891 ..
9892 }
9893 ));
9894 }
9895
9896 #[test]
9897 fn vector_cast_postfix_wraps_string_literal() {
9898 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9899 let Statement::Select(s) = s else { panic!() };
9900 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9901 panic!()
9902 };
9903 assert!(matches!(
9904 expr,
9905 Expr::Cast {
9906 target: CastTarget::Vector,
9907 ..
9908 }
9909 ));
9910 }
9911
9912 #[test]
9913 fn unsupported_cast_target_errors() {
9914 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
9916 assert!(err.message.contains("unsupported cast target"));
9917 }
9918
9919 #[test]
9920 fn tx_statements_round_trip() {
9921 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
9922 let original = parse(q);
9923 let again = parse_statement(&original.to_string()).unwrap();
9924 assert_eq!(original, again);
9925 }
9926 }
9927
9928 #[test]
9929 fn interval_text_parsing_units() {
9930 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
9932 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
9933 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
9934 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
9935 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
9937 assert_eq!(
9938 parse_interval_text("1 day 2 hours"),
9939 Some((0, 86_400_000_000 + 7_200_000_000))
9940 );
9941 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
9943 assert_eq!(parse_interval_text(""), None);
9945 assert_eq!(parse_interval_text("garbage"), None);
9946 assert_eq!(parse_interval_text("1 fortnight"), None);
9947 assert_eq!(parse_interval_text("1"), None);
9948 }
9949
9950 #[test]
9951 fn interval_literal_roundtrips_via_display() {
9952 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
9953 let s = parsed.to_string();
9954 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
9956 let again = parse_statement(&s).unwrap();
9958 assert_eq!(parsed, again);
9959 }
9960
9961 #[test]
9964 fn parser_recognises_create_publication_bare() {
9965 let s = parse("CREATE PUBLICATION pub_a");
9966 let Statement::CreatePublication(p) = s else {
9967 panic!("expected CreatePublication, got {s:?}")
9968 };
9969 assert_eq!(p.name, "pub_a");
9970 assert_eq!(p.scope, PublicationScope::AllTables);
9971 }
9972
9973 #[test]
9974 fn parser_recognises_create_publication_for_all_tables() {
9975 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
9976 let Statement::CreatePublication(p) = s else {
9977 panic!("expected CreatePublication, got {s:?}")
9978 };
9979 assert_eq!(p.name, "pub_a");
9980 assert_eq!(p.scope, PublicationScope::AllTables);
9981 }
9982
9983 #[test]
9984 fn parser_recognises_drop_publication() {
9985 let s = parse("DROP PUBLICATION pub_a");
9986 let Statement::DropPublication(name) = s else {
9987 panic!("expected DropPublication, got {s:?}")
9988 };
9989 assert_eq!(name, "pub_a");
9990 }
9991
9992 #[test]
9993 fn parser_recognises_for_table_list() {
9994 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
9995 let Statement::CreatePublication(p) = s else {
9996 panic!("expected CreatePublication, got {s:?}")
9997 };
9998 assert_eq!(p.name, "pub_a");
9999 let PublicationScope::ForTables(ts) = p.scope else {
10000 panic!("expected ForTables scope")
10001 };
10002 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
10003 }
10004
10005 #[test]
10006 fn parser_recognises_for_tables_plural() {
10007 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
10009 let Statement::CreatePublication(p) = s else {
10010 panic!("expected CreatePublication, got {s:?}")
10011 };
10012 let PublicationScope::ForTables(ts) = p.scope else {
10013 panic!("expected ForTables")
10014 };
10015 assert_eq!(ts, alloc::vec!["t1", "t2"]);
10016 }
10017
10018 #[test]
10019 fn parser_recognises_for_all_tables_except_list() {
10020 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
10021 let Statement::CreatePublication(p) = s else {
10022 panic!()
10023 };
10024 let PublicationScope::AllTablesExcept(ts) = p.scope else {
10025 panic!("expected AllTablesExcept")
10026 };
10027 assert_eq!(ts, alloc::vec!["t1", "t2"]);
10028 }
10029
10030 #[test]
10031 fn parser_rejects_for_table_with_empty_list() {
10032 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
10034 .expect_err("must error on empty list");
10035 assert!(!err.message.is_empty());
10038 }
10039
10040 #[test]
10041 fn parser_recognises_show_publications() {
10042 let s = parse("SHOW PUBLICATIONS");
10045 assert!(matches!(s, Statement::ShowPublications));
10046 }
10047
10048 #[test]
10051 fn parser_recognises_create_subscription_single_publication() {
10052 let s = parse(
10053 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
10054 );
10055 let Statement::CreateSubscription(c) = s else {
10056 panic!("expected CreateSubscription, got {s:?}")
10057 };
10058 assert_eq!(c.name, "sub_a");
10059 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
10060 assert_eq!(c.publications, alloc::vec!["pub_a"]);
10061 }
10062
10063 #[test]
10064 fn parser_recognises_create_subscription_multi_publication() {
10065 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
10066 let Statement::CreateSubscription(c) = s else {
10067 panic!()
10068 };
10069 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
10070 }
10071
10072 #[test]
10073 fn parser_rejects_create_subscription_missing_connection() {
10074 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
10075 .expect_err("must error on missing CONNECTION");
10076 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
10077 }
10078
10079 #[test]
10080 fn parser_rejects_create_subscription_missing_publication() {
10081 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
10082 .expect_err("must error on missing PUBLICATION");
10083 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
10084 }
10085
10086 #[test]
10087 fn parser_recognises_drop_subscription() {
10088 let s = parse("DROP SUBSCRIPTION sub_a");
10089 let Statement::DropSubscription(name) = s else {
10090 panic!("expected DropSubscription, got {s:?}")
10091 };
10092 assert_eq!(name, "sub_a");
10093 }
10094
10095 #[test]
10096 fn parser_recognises_show_subscriptions() {
10097 let s = parse("SHOW SUBSCRIPTIONS");
10098 assert!(matches!(s, Statement::ShowSubscriptions));
10099 }
10100
10101 #[test]
10102 fn parser_recognises_wait_for_wal_position_no_timeout() {
10103 let s = parse("WAIT FOR WAL POSITION 12345");
10104 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
10105 panic!("expected WaitForWalPosition, got {s:?}")
10106 };
10107 assert_eq!(pos, 12345);
10108 assert!(timeout_ms.is_none());
10109 }
10110
10111 #[test]
10112 fn parser_recognises_wait_for_wal_position_with_timeout() {
10113 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
10114 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
10115 panic!()
10116 };
10117 assert_eq!(pos, 67890);
10118 assert_eq!(timeout_ms, Some(5000));
10119 }
10120
10121 #[test]
10122 fn parser_rejects_wait_with_negative_position() {
10123 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
10129 assert!(!err.message.is_empty());
10130 }
10131
10132 #[test]
10133 fn parser_recognises_bare_analyze() {
10134 let s = parse("ANALYZE");
10135 assert!(matches!(s, Statement::Analyze(None)));
10136 }
10137
10138 #[test]
10139 fn parser_recognises_analyze_with_table() {
10140 let s = parse("ANALYZE users");
10141 let Statement::Analyze(Some(name)) = s else {
10142 panic!("expected Analyze, got {s:?}")
10143 };
10144 assert_eq!(name, "users");
10145 }
10146
10147 #[test]
10148 fn parser_recognises_analyze_with_quoted_table() {
10149 let s = parse("ANALYZE \"Mixed Case\"");
10150 let Statement::Analyze(Some(name)) = s else {
10151 panic!()
10152 };
10153 assert_eq!(name, "Mixed Case");
10154 }
10155
10156 #[test]
10157 fn parser_rejects_analyze_with_garbage_token() {
10158 let err = parse_statement("ANALYZE 42").expect_err("must error");
10159 assert!(!err.message.is_empty());
10160 }
10161
10162 #[test]
10163 fn analyze_display_roundtrips() {
10164 for sql in ["ANALYZE", "ANALYZE users"] {
10165 let s = parse(sql);
10166 let printed = s.to_string();
10167 let again = parse_statement(&printed)
10168 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10169 assert_eq!(s, again);
10170 }
10171 }
10172
10173 #[test]
10174 fn wait_for_display_roundtrips() {
10175 for sql in [
10176 "WAIT FOR WAL POSITION 12345",
10177 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
10178 ] {
10179 let s = parse(sql);
10180 let printed = s.to_string();
10181 let again = parse_statement(&printed)
10182 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10183 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10184 }
10185 }
10186
10187 #[test]
10188 fn subscription_ddl_display_roundtrips() {
10189 for sql in [
10190 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
10191 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
10192 "DROP SUBSCRIPTION sub_a",
10193 "SHOW SUBSCRIPTIONS",
10194 ] {
10195 let s = parse(sql);
10196 let printed = s.to_string();
10197 let again = parse_statement(&printed)
10198 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10199 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10200 }
10201 }
10202
10203 #[test]
10204 fn parser_drop_dispatches_user_vs_publication() {
10205 let s = parse("DROP USER 'alice'");
10208 let Statement::DropUser(name) = s else {
10209 panic!("expected DropUser, got {s:?}")
10210 };
10211 assert_eq!(name, "alice");
10212 let s = parse("DROP PUBLICATION p1");
10214 assert!(matches!(s, Statement::DropPublication(_)));
10215 }
10216
10217 #[test]
10218 fn publication_ddl_display_roundtrips() {
10219 for sql in [
10222 "CREATE PUBLICATION pub_a",
10223 "CREATE PUBLICATION pub_a FOR ALL TABLES",
10224 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
10225 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
10226 "DROP PUBLICATION pub_a",
10227 "SHOW PUBLICATIONS",
10228 ] {
10229 let s = parse(sql);
10230 let printed = s.to_string();
10231 let again = parse_statement(&printed)
10232 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10233 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10234 }
10235 }
10236
10237 #[test]
10240 fn create_function_returns_trigger_plpgsql_minimal() {
10241 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
10242 let s = parse(sql);
10243 let Statement::CreateFunction(f) = s else {
10244 panic!("expected CreateFunction");
10245 };
10246 assert_eq!(f.name, "noop");
10247 assert!(!f.or_replace);
10248 assert!(f.args.is_empty());
10249 assert!(matches!(f.returns, FunctionReturn::Trigger));
10250 assert_eq!(f.language, "plpgsql");
10251 let FunctionBody::PlPgSql(block) = f.body else {
10252 panic!("expected PlPgSql body");
10253 };
10254 assert_eq!(block.statements.len(), 1);
10255 assert!(matches!(
10256 block.statements[0],
10257 PlPgSqlStmt::Return(ReturnTarget::New)
10258 ));
10259 }
10260
10261 #[test]
10262 fn create_function_or_replace_with_assignment() {
10263 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
10266BEGIN
10267 NEW.search_vector := to_tsvector('english', NEW.subject);
10268 RETURN NEW;
10269END;
10270$$";
10271 let s = parse(sql);
10272 let Statement::CreateFunction(f) = s else {
10273 panic!("expected CreateFunction");
10274 };
10275 assert!(f.or_replace);
10276 let FunctionBody::PlPgSql(block) = &f.body else {
10277 panic!("expected PlPgSql body");
10278 };
10279 assert_eq!(block.statements.len(), 2);
10280 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
10282 panic!("expected Assign as first stmt");
10283 };
10284 match target {
10285 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
10286 other => panic!("expected NEW.col, got {other:?}"),
10287 }
10288 assert!(matches!(
10290 block.statements[1],
10291 PlPgSqlStmt::Return(ReturnTarget::New)
10292 ));
10293 }
10294
10295 #[test]
10296 fn create_trigger_after_insert_or_update() {
10297 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
10298 let s = parse(sql);
10299 let Statement::CreateTrigger(t) = s else {
10300 panic!("expected CreateTrigger");
10301 };
10302 assert_eq!(t.name, "tg");
10303 assert_eq!(t.table, "messages");
10304 assert_eq!(t.timing, TriggerTiming::After);
10305 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
10306 assert_eq!(t.for_each, TriggerForEach::Row);
10307 assert_eq!(t.function, "update_sv");
10308 }
10309
10310 #[test]
10311 fn create_trigger_before_delete_execute_procedure_alias() {
10312 let sql =
10314 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
10315 let s = parse(sql);
10316 let Statement::CreateTrigger(t) = s else {
10317 panic!("expected CreateTrigger");
10318 };
10319 assert_eq!(t.timing, TriggerTiming::Before);
10320 assert_eq!(t.events, vec![TriggerEvent::Delete]);
10321 }
10322
10323 #[test]
10324 fn drop_trigger_if_exists_round_trips() {
10325 let s = Statement::DropTrigger {
10330 name: "tg".into(),
10331 table: "messages".into(),
10332 if_exists: true,
10333 };
10334 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
10335 }
10336
10337 #[test]
10338 fn trigger_ddl_display_roundtrips_through_parser() {
10339 for sql in [
10343 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
10344 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
10345 ] {
10346 let s = parse(sql);
10347 let printed = s.to_string();
10348 let again = parse_statement(&printed)
10349 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10350 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10351 }
10352 }
10353}