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_type",
4332 "pg_user",
4333 "pg_views",
4334 ];
4335 let name = match self.tokens.get(self.pos) {
4336 Some(Token::Ident(s)) => s.to_ascii_lowercase(),
4337 _ => return None,
4338 };
4339 if matches!(self.tokens.get(self.pos + 1), Some(Token::Dot)) {
4342 return None;
4343 }
4344 if !PG_META_TABLES.contains(&name.as_str()) {
4345 return None;
4346 }
4347 self.advance();
4348 let bare = name.strip_prefix("pg_").unwrap_or(&name);
4349 Some(alloc::format!("__spg_pg_{bare}"))
4350 }
4351
4352 fn expect_keyword_ident(&mut self, kw: &str) -> Result<(), ParseError> {
4354 match self.advance() {
4355 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case(kw) => Ok(()),
4356 other => Err(ParseError {
4357 message: format!("expected {kw:?}, got {other:?}"),
4358 token_pos: self.pos.saturating_sub(1),
4359 }),
4360 }
4361 }
4362
4363 fn expect_ident_or_string(&mut self) -> Result<String, ParseError> {
4367 match self.advance() {
4368 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => Ok(s),
4369 other => Err(ParseError {
4370 message: format!("expected identifier or string, got {other:?}"),
4371 token_pos: self.pos.saturating_sub(1),
4372 }),
4373 }
4374 }
4375
4376 fn expect_string_literal(&mut self) -> Result<String, ParseError> {
4377 match self.advance() {
4378 Token::String(s) => Ok(s),
4379 other => Err(ParseError {
4380 message: format!("expected quoted string, got {other:?}"),
4381 token_pos: self.pos.saturating_sub(1),
4382 }),
4383 }
4384 }
4385
4386 fn parse_select_stmt(&mut self) -> Result<Statement, ParseError> {
4387 let mut head = self.parse_bare_select()?;
4392 while matches!(self.peek(), Token::Union) {
4393 self.advance();
4394 let kind = if matches!(self.peek(), Token::All) {
4395 self.advance();
4396 UnionKind::All
4397 } else {
4398 UnionKind::Distinct
4399 };
4400 let peer = self.parse_bare_select()?;
4401 head.unions.push((kind, peer));
4402 }
4403 head.order_by = if matches!(self.peek(), Token::Order) {
4404 self.advance();
4405 if !matches!(self.peek(), Token::By) {
4406 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
4407 }
4408 self.advance();
4409 let mut keys = Vec::new();
4412 loop {
4413 let expr = self.parse_expr(0)?;
4414 let desc = if matches!(self.peek(), Token::Desc) {
4415 self.advance();
4416 true
4417 } else if matches!(self.peek(), Token::Asc) {
4418 self.advance();
4419 false
4420 } else {
4421 false
4422 };
4423 keys.push(OrderBy { expr, desc });
4424 if matches!(self.peek(), Token::Comma) {
4425 self.advance();
4426 } else {
4427 break;
4428 }
4429 }
4430 keys
4431 } else {
4432 Vec::new()
4433 };
4434 head.limit = if matches!(self.peek(), Token::Limit) {
4435 self.advance();
4436 if self.consume_limit_unbounded_sentinel() {
4443 None
4444 } else {
4445 Some(self.parse_limit_expr("LIMIT")?)
4446 }
4447 } else {
4448 None
4449 };
4450 head.offset = if matches!(self.peek(), Token::Offset) {
4451 self.advance();
4452 let off = self.parse_limit_expr("OFFSET")?;
4456 self.consume_optional_rows_keyword();
4457 Some(off)
4458 } else {
4459 None
4460 };
4461 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("fetch"))
4467 {
4468 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4471 if s.eq_ignore_ascii_case("first") || s.eq_ignore_ascii_case("next"))
4472 {
4473 self.advance();
4474 }
4475 let count = if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4478 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4479 {
4480 crate::ast::LimitExpr::Literal(1)
4482 } else {
4483 self.parse_limit_expr("FETCH FIRST")?
4484 };
4485 self.consume_optional_rows_keyword();
4487 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4493 if s.eq_ignore_ascii_case("only"))
4494 {
4495 self.advance();
4496 } else if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4497 if s.eq_ignore_ascii_case("with"))
4498 {
4499 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4501 if s.eq_ignore_ascii_case("ties"))
4502 {
4503 self.advance();
4504 head.limit_with_ties = true;
4505 }
4506 }
4507 head.limit = Some(count);
4508 }
4509 self.consume_optional_for_lock_clauses();
4524 Ok(Statement::Select(head))
4525 }
4526
4527 fn consume_optional_for_lock_clauses(&mut self) {
4534 while matches!(self.peek(), Token::For) {
4535 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4539 if s.eq_ignore_ascii_case("no"))
4540 {
4541 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4545 if s.eq_ignore_ascii_case("key"))
4546 {
4547 self.advance(); }
4549 }
4550 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4552 if s.eq_ignore_ascii_case("key"))
4553 {
4554 self.advance(); }
4556 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4562 if s.eq_ignore_ascii_case("update") || s.eq_ignore_ascii_case("share"))
4563 {
4564 self.advance();
4565 } else {
4566 return;
4571 }
4572 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4575 if s.eq_ignore_ascii_case("of"))
4576 {
4577 self.advance(); #[allow(clippy::while_let_loop)]
4579 loop {
4580 match self.peek() {
4581 Token::Ident(_) | Token::QuotedIdent(_) => {
4582 self.advance();
4583 if matches!(self.peek(), Token::Dot) {
4585 self.advance();
4586 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4587 self.advance();
4588 }
4589 }
4590 }
4591 _ => break,
4592 }
4593 if matches!(self.peek(), Token::Comma) {
4594 self.advance();
4595 } else {
4596 break;
4597 }
4598 }
4599 }
4600 match self.peek().clone() {
4602 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nowait") => {
4603 self.advance();
4604 }
4605 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("skip") => {
4606 self.advance(); if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4608 if s.eq_ignore_ascii_case("locked"))
4609 {
4610 self.advance(); }
4612 }
4613 _ => {}
4614 }
4615 }
4617 }
4618
4619 fn consume_limit_unbounded_sentinel(&mut self) -> bool {
4628 if matches!(self.peek(), Token::Null) {
4629 self.advance();
4630 return true;
4631 }
4632 if matches!(self.peek(), Token::All) {
4633 self.advance();
4634 return true;
4635 }
4636 false
4637 }
4638
4639 fn consume_optional_rows_keyword(&mut self) {
4643 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s)
4644 if s.eq_ignore_ascii_case("row") || s.eq_ignore_ascii_case("rows"))
4645 {
4646 self.advance();
4647 }
4648 }
4649
4650 fn parse_limit_expr(&mut self, label: &str) -> Result<crate::ast::LimitExpr, ParseError> {
4651 match self.advance() {
4652 Token::Integer(n) if n >= 0 => u32::try_from(n)
4653 .map(crate::ast::LimitExpr::Literal)
4654 .map_err(|_| ParseError {
4655 message: alloc::format!("{label} value too large: {n}"),
4656 token_pos: self.pos.saturating_sub(1),
4657 }),
4658 Token::Placeholder(n) => Ok(crate::ast::LimitExpr::Placeholder(n)),
4659 other => Err(ParseError {
4660 message: alloc::format!(
4661 "expected non-negative integer or $N placeholder after {label}, got {other:?}"
4662 ),
4663 token_pos: self.pos.saturating_sub(1),
4664 }),
4665 }
4666 }
4667
4668 fn parse_bare_select(&mut self) -> Result<SelectStatement, ParseError> {
4673 if !matches!(self.peek(), Token::Select) {
4674 return Err(self.err(format!(
4675 "expected SELECT to start a query block, got {:?}",
4676 self.peek()
4677 )));
4678 }
4679 self.advance();
4680 let distinct = if matches!(self.peek(), Token::Distinct) {
4681 self.advance();
4682 true
4683 } else {
4684 false
4685 };
4686 let items = self.parse_select_list()?;
4687 let from = if matches!(self.peek(), Token::From) {
4688 self.advance();
4689 Some(self.parse_from_clause()?)
4690 } else {
4691 None
4692 };
4693 let where_ = if matches!(self.peek(), Token::Where) {
4694 self.advance();
4695 Some(self.parse_expr(0)?)
4696 } else {
4697 None
4698 };
4699 let mut group_by_all = false;
4700 let group_by = if matches!(self.peek(), Token::Group) {
4701 self.advance();
4702 if !matches!(self.peek(), Token::By) {
4703 return Err(self.err(format!("expected BY after GROUP, got {:?}", self.peek())));
4704 }
4705 self.advance();
4706 if matches!(self.peek(), Token::All) {
4709 self.advance();
4710 group_by_all = true;
4711 None
4712 } else {
4713 let mut groups = Vec::new();
4714 loop {
4715 groups.push(self.parse_expr(0)?);
4716 if matches!(self.peek(), Token::Comma) {
4717 self.advance();
4718 } else {
4719 break;
4720 }
4721 }
4722 Some(groups)
4723 }
4724 } else {
4725 None
4726 };
4727 let having = if matches!(self.peek(), Token::Having) {
4728 self.advance();
4729 Some(self.parse_expr(0)?)
4730 } else {
4731 None
4732 };
4733 Ok(SelectStatement {
4734 ctes: Vec::new(),
4735 distinct,
4736 items,
4737 from,
4738 where_,
4739 group_by,
4740 group_by_all,
4741 having,
4742 unions: Vec::new(),
4743 order_by: Vec::new(),
4744 limit: None,
4745 offset: None,
4746 limit_with_ties: false,
4747 })
4748 }
4749
4750 fn parse_create_table_stmt_after_create(&mut self) -> Result<Statement, ParseError> {
4751 debug_assert!(matches!(self.peek(), Token::Table));
4753 self.advance();
4754 let if_not_exists = self.consume_if_not_exists();
4755 let name = self.expect_ident_like()?;
4756 if !matches!(self.peek(), Token::LParen) {
4757 return Err(self.err(format!(
4758 "expected '(' after table name, got {:?}",
4759 self.peek()
4760 )));
4761 }
4762 self.advance();
4763 let mut columns = Vec::new();
4764 let mut foreign_keys: Vec<ForeignKeyConstraint> = Vec::new();
4765 let mut table_constraints: Vec<crate::ast::TableConstraint> = Vec::new();
4766 loop {
4767 if self.peek_table_level_pk_start() {
4773 table_constraints.push(self.parse_table_level_primary_key()?);
4774 } else if self.peek_table_level_unique_start() {
4775 table_constraints.push(self.parse_table_level_unique()?);
4776 } else if self.peek_table_level_check_start() {
4777 table_constraints.push(self.parse_table_level_check()?);
4779 } else if self.peek_mysql_inline_key_start() {
4780 if let Some(uc) = self.parse_mysql_inline_key()? {
4786 table_constraints.push(uc);
4787 }
4788 } else if let Some(kind) = self.peek_named_table_constraint_kind() {
4789 self.advance(); let _name = self.expect_ident_like()?;
4797 table_constraints.push(match kind {
4798 NamedTableConstraintKind::Check => self.parse_table_level_check()?,
4799 NamedTableConstraintKind::Unique => self.parse_table_level_unique()?,
4800 NamedTableConstraintKind::PrimaryKey => self.parse_table_level_primary_key()?,
4801 });
4802 } else if self.peek_constraint_or_fk_start() {
4803 foreign_keys.push(self.parse_table_level_fk()?);
4804 } else {
4805 let (col, col_level_fk) = self.parse_column_def_with_fk()?;
4806 if col.is_unique {
4810 table_constraints.push(crate::ast::TableConstraint::Unique {
4811 name: None,
4812 columns: alloc::vec![col.name.clone()],
4813 nulls_not_distinct: false,
4814 });
4815 }
4816 if let Some(check_expr) = col.check.clone() {
4817 table_constraints.push(crate::ast::TableConstraint::Check {
4818 name: None,
4819 expr: check_expr,
4820 });
4821 }
4822 columns.push(col);
4823 if let Some(fk) = col_level_fk {
4824 foreign_keys.push(fk);
4825 }
4826 }
4827 match self.peek() {
4828 Token::Comma => {
4829 self.advance();
4830 }
4831 Token::RParen => {
4832 self.advance();
4833 break;
4834 }
4835 other => {
4836 return Err(
4837 self.err(format!("expected ',' or ')' in column list, got {other:?}"))
4838 );
4839 }
4840 }
4841 }
4842 if columns.is_empty() {
4843 return Err(self.err("CREATE TABLE requires at least one column".into()));
4844 }
4845 self.consume_mysql_table_options();
4852 Ok(Statement::CreateTable(CreateTableStatement {
4853 name,
4854 columns,
4855 if_not_exists,
4856 foreign_keys,
4857 table_constraints,
4858 }))
4859 }
4860
4861 fn peek_mysql_inline_key_start(&self) -> bool {
4870 let cur = self.peek();
4871 let after_keyword_followed_by_paren_or_ident_paren = |skip: usize| -> bool {
4881 match self.tokens.get(skip) {
4884 Some(Token::LParen) => true,
4885 Some(Token::Ident(_) | Token::QuotedIdent(_)) => {
4886 matches!(self.tokens.get(skip + 1), Some(Token::LParen))
4887 }
4888 _ => false,
4889 }
4890 };
4891 let is_key_or_index_tok = |t: &Token| -> bool {
4895 matches!(t, Token::Index)
4896 || matches!(t, Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index"))
4897 };
4898 match cur {
4899 Token::Index => after_keyword_followed_by_paren_or_ident_paren(self.pos + 1),
4900 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4901 after_keyword_followed_by_paren_or_ident_paren(self.pos + 1)
4902 }
4903 Token::Ident(s)
4904 if s.eq_ignore_ascii_case("fulltext") || s.eq_ignore_ascii_case("spatial") =>
4905 {
4906 let nxt = self.tokens.get(self.pos + 1);
4907 let after_after = if nxt.is_some_and(is_key_or_index_tok) {
4908 self.pos + 2
4909 } else {
4910 self.pos + 1
4911 };
4912 after_keyword_followed_by_paren_or_ident_paren(after_after)
4913 }
4914 Token::Ident(s) if s.eq_ignore_ascii_case("unique") => {
4915 let nxt = self.tokens.get(self.pos + 1);
4916 if !nxt.is_some_and(is_key_or_index_tok) {
4917 return false;
4918 }
4919 after_keyword_followed_by_paren_or_ident_paren(self.pos + 2)
4920 }
4921 _ => false,
4922 }
4923 }
4924
4925 fn parse_mysql_inline_key(
4934 &mut self,
4935 ) -> Result<Option<crate::ast::TableConstraint>, ParseError> {
4936 let is_unique = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unique"))
4938 {
4939 self.advance();
4940 true
4941 } else {
4942 false
4943 };
4944 let mut is_fulltext = false;
4950 let mut is_spatial = false;
4951 if let Token::Ident(s) = self.peek().clone() {
4952 if s.eq_ignore_ascii_case("fulltext") {
4953 self.advance();
4954 is_fulltext = true;
4955 } else if s.eq_ignore_ascii_case("spatial") {
4956 self.advance();
4957 is_spatial = true;
4958 }
4959 }
4960 match self.peek() {
4963 Token::Index => {
4964 self.advance();
4965 }
4966 Token::Ident(s) if s.eq_ignore_ascii_case("key") || s.eq_ignore_ascii_case("index") => {
4967 self.advance();
4968 }
4969 other => {
4970 return Err(self.err(alloc::format!(
4971 "expected KEY/INDEX in inline index declaration, got {other:?}"
4972 )));
4973 }
4974 }
4975 let mut idx_name: Option<String> = None;
4980 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_))
4981 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
4982 {
4983 if let Token::Ident(s) | Token::QuotedIdent(s) = self.advance() {
4984 idx_name = Some(s);
4985 }
4986 }
4987 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
4989 self.advance();
4990 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
4991 self.advance();
4992 }
4993 }
4994 if !matches!(self.peek(), Token::LParen) {
4996 return Err(self.err(alloc::format!(
4997 "expected '(' in inline KEY/INDEX, got {:?}",
4998 self.peek()
4999 )));
5000 }
5001 self.advance();
5002 let mut cols: Vec<String> = Vec::new();
5003 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
5004 self.advance();
5005 cols.push(s);
5006 if matches!(self.peek(), Token::LParen) {
5008 let mut depth = 1usize;
5009 self.advance();
5010 while depth > 0 {
5011 match self.peek() {
5012 Token::LParen => depth += 1,
5013 Token::RParen => depth -= 1,
5014 Token::Eof => break,
5015 _ => {}
5016 }
5017 self.advance();
5018 }
5019 }
5020 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("asc") || s.eq_ignore_ascii_case("desc"))
5022 || matches!(self.peek(), Token::Asc | Token::Desc)
5023 {
5024 self.advance();
5025 }
5026 if matches!(self.peek(), Token::Comma) {
5027 self.advance();
5028 continue;
5029 }
5030 break;
5031 }
5032 if matches!(self.peek(), Token::RParen) {
5033 self.advance();
5034 }
5035 while !matches!(self.peek(), Token::Comma | Token::RParen | Token::Eof) {
5038 self.advance();
5039 }
5040 if cols.is_empty() {
5041 return Ok(None);
5042 }
5043 if is_unique {
5044 Ok(Some(crate::ast::TableConstraint::Unique {
5050 name: idx_name,
5051 columns: cols,
5052 nulls_not_distinct: false,
5053 }))
5054 } else if is_fulltext {
5055 Ok(Some(crate::ast::TableConstraint::FulltextIndex {
5061 name: idx_name,
5062 columns: cols,
5063 }))
5064 } else if is_spatial {
5065 Ok(None)
5068 } else {
5069 Ok(Some(crate::ast::TableConstraint::Index {
5072 name: idx_name,
5073 columns: cols,
5074 }))
5075 }
5076 }
5077
5078 fn consume_mysql_table_options(&mut self) {
5083 loop {
5084 let name_lc = match self.peek().clone() {
5088 Token::Ident(s) | Token::QuotedIdent(s) => s.to_ascii_lowercase(),
5089 Token::Default => alloc::string::String::from("default"),
5090 _ => break,
5091 };
5092 let known = matches!(
5093 name_lc.as_str(),
5094 "engine"
5095 | "default"
5096 | "charset"
5097 | "collate"
5098 | "auto_increment"
5099 | "row_format"
5100 | "comment"
5101 | "pack_keys"
5102 | "stats_persistent"
5103 | "stats_auto_recalc"
5104 | "stats_sample_pages"
5105 | "key_block_size"
5106 | "tablespace"
5107 | "min_rows"
5108 | "max_rows"
5109 | "checksum"
5110 | "delay_key_write"
5111 | "insert_method"
5112 | "data"
5113 | "index"
5114 | "encryption"
5115 | "compression"
5116 );
5117 if !known {
5118 break;
5119 }
5120 self.advance(); if name_lc == "default" {
5124 if matches!(self.peek(), Token::Ident(_) | Token::QuotedIdent(_)) {
5125 self.advance();
5126 }
5127 }
5128 if matches!(self.peek(), Token::Eq) {
5129 self.advance();
5130 }
5131 match self.peek() {
5132 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_) | Token::Integer(_) => {
5133 self.advance();
5134 }
5135 _ => {}
5136 }
5137 }
5138 }
5139
5140 fn peek_table_level_pk_start(&self) -> bool {
5145 let cur = self.peek();
5146 let nxt = self.tokens.get(self.pos + 1);
5147 let nxt2 = self.tokens.get(self.pos + 2);
5148 let is_primary = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("primary"));
5149 let is_key = matches!(nxt, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("key"));
5150 let is_lparen = matches!(nxt2, Some(Token::LParen));
5151 is_primary && is_key && is_lparen
5152 }
5153
5154 fn peek_table_level_unique_start(&self) -> bool {
5158 let cur = self.peek();
5159 let is_unique = matches!(cur, Token::Ident(s) if s.eq_ignore_ascii_case("unique"));
5160 if !is_unique {
5161 return false;
5162 }
5163 let n1 = self.tokens.get(self.pos + 1);
5164 if matches!(n1, Some(Token::LParen)) {
5166 return true;
5167 }
5168 let is_nulls = matches!(n1, Some(Token::Ident(s)) if s.eq_ignore_ascii_case("nulls"));
5170 if !is_nulls {
5171 return false;
5172 }
5173 let n2 = self.tokens.get(self.pos + 2);
5174 let n3 = self.tokens.get(self.pos + 3);
5175 let n4 = self.tokens.get(self.pos + 4);
5176 if matches!(n2, Some(Token::Distinct)) && matches!(n3, Some(Token::LParen)) {
5178 return true;
5179 }
5180 if matches!(n2, Some(Token::Not))
5182 && matches!(n3, Some(Token::Distinct))
5183 && matches!(n4, Some(Token::LParen))
5184 {
5185 return true;
5186 }
5187 false
5188 }
5189
5190 fn parse_table_level_primary_key(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5191 self.advance(); self.advance(); let columns = self.parse_paren_ident_list("PRIMARY KEY")?;
5194 Ok(crate::ast::TableConstraint::PrimaryKey {
5195 name: None,
5196 columns,
5197 })
5198 }
5199
5200 fn parse_table_level_unique(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5201 self.advance(); let mut nulls_not_distinct = false;
5206 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("nulls")) {
5207 let n1 = self.tokens.get(self.pos + 1);
5208 let n2 = self.tokens.get(self.pos + 2);
5209 let is_not = matches!(n1, Some(Token::Not));
5210 let is_distinct = matches!(n2, Some(Token::Distinct));
5211 if is_not && is_distinct {
5212 self.advance(); self.advance(); self.advance(); nulls_not_distinct = true;
5216 } else if matches!(n1, Some(Token::Distinct)) {
5217 self.advance(); self.advance(); }
5220 }
5221 let columns = self.parse_paren_ident_list("UNIQUE")?;
5222 Ok(crate::ast::TableConstraint::Unique {
5223 name: None,
5224 columns,
5225 nulls_not_distinct,
5226 })
5227 }
5228
5229 fn parse_table_level_check(&mut self) -> Result<crate::ast::TableConstraint, ParseError> {
5233 self.advance(); if !matches!(self.peek(), Token::LParen) {
5235 return Err(self.err(alloc::format!(
5236 "expected '(' after CHECK, got {:?}",
5237 self.peek()
5238 )));
5239 }
5240 self.advance();
5241 let expr = self.parse_expr(0)?;
5242 if !matches!(self.peek(), Token::RParen) {
5243 return Err(self.err(alloc::format!(
5244 "expected ')' to close CHECK predicate, got {:?}",
5245 self.peek()
5246 )));
5247 }
5248 self.advance();
5249 Ok(crate::ast::TableConstraint::Check { name: None, expr })
5250 }
5251
5252 fn peek_table_level_check_start(&self) -> bool {
5254 matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("check"))
5255 }
5256
5257 fn peek_named_table_constraint_kind(&self) -> Option<NamedTableConstraintKind> {
5262 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5263 return None;
5264 }
5265 match self.tokens.get(self.pos + 2) {
5268 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("check") => {
5269 Some(NamedTableConstraintKind::Check)
5270 }
5271 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("unique") => {
5272 Some(NamedTableConstraintKind::Unique)
5273 }
5274 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("primary") => {
5275 Some(NamedTableConstraintKind::PrimaryKey)
5276 }
5277 _ => None,
5278 }
5279 }
5280
5281 fn parse_paren_ident_list(&mut self, ctx: &str) -> Result<Vec<String>, ParseError> {
5282 if !matches!(self.peek(), Token::LParen) {
5283 return Err(self.err(alloc::format!(
5284 "expected '(' after {ctx}, got {:?}",
5285 self.peek()
5286 )));
5287 }
5288 self.advance();
5289 let mut out = Vec::new();
5290 loop {
5291 out.push(self.expect_ident_like()?);
5292 match self.peek() {
5293 Token::Comma => {
5294 self.advance();
5295 }
5296 Token::RParen => {
5297 self.advance();
5298 break;
5299 }
5300 other => {
5301 return Err(self.err(alloc::format!(
5302 "expected ',' or ')' in {ctx} list, got {other:?}"
5303 )));
5304 }
5305 }
5306 }
5307 if out.is_empty() {
5308 return Err(self.err(alloc::format!("{ctx} requires at least one column")));
5309 }
5310 Ok(out)
5311 }
5312
5313 fn peek_constraint_or_fk_start(&self) -> bool {
5318 let is_constraint_kw = matches!(
5319 self.peek(),
5320 Token::Ident(s) if s.eq_ignore_ascii_case("constraint")
5321 );
5322 let is_foreign_kw = matches!(
5323 self.peek(),
5324 Token::Ident(s) if s.eq_ignore_ascii_case("foreign")
5325 );
5326 is_constraint_kw || is_foreign_kw
5327 }
5328
5329 fn parse_table_level_fk(&mut self) -> Result<ForeignKeyConstraint, ParseError> {
5333 let mut name: Option<String> = None;
5334 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
5335 self.advance();
5336 name = Some(self.expect_ident_like()?);
5337 }
5338 match self.advance() {
5340 Token::Ident(s) if s.eq_ignore_ascii_case("foreign") => {}
5341 other => return Err(self.err(format!("expected FOREIGN, got {other:?}"))),
5342 }
5343 match self.advance() {
5345 Token::Ident(s) if s.eq_ignore_ascii_case("key") => {}
5346 other => return Err(self.err(format!("expected KEY after FOREIGN, got {other:?}"))),
5347 }
5348 if !matches!(self.peek(), Token::LParen) {
5350 return Err(self.err(format!(
5351 "expected '(' after FOREIGN KEY, got {:?}",
5352 self.peek()
5353 )));
5354 }
5355 self.advance();
5356 let mut columns = Vec::new();
5357 loop {
5358 columns.push(self.expect_ident_like()?);
5359 match self.peek() {
5360 Token::Comma => {
5361 self.advance();
5362 }
5363 Token::RParen => {
5364 self.advance();
5365 break;
5366 }
5367 other => {
5368 return Err(self.err(format!(
5369 "expected ',' or ')' in FK column list, got {other:?}"
5370 )));
5371 }
5372 }
5373 }
5374 if columns.is_empty() {
5375 return Err(self.err("FOREIGN KEY requires at least one column".into()));
5376 }
5377 let (parent_table, parent_columns, on_delete, on_update) =
5378 self.parse_references_tail(columns.len())?;
5379 Ok(ForeignKeyConstraint {
5380 name,
5381 columns,
5382 parent_table,
5383 parent_columns,
5384 on_delete,
5385 on_update,
5386 })
5387 }
5388
5389 fn parse_references_tail(
5394 &mut self,
5395 expected_arity: usize,
5396 ) -> Result<(String, Vec<String>, FkAction, FkAction), ParseError> {
5397 match self.advance() {
5398 Token::Ident(s) if s.eq_ignore_ascii_case("references") => {}
5399 other => return Err(self.err(format!("expected REFERENCES, got {other:?}"))),
5400 }
5401 let parent_table = self.expect_ident_like()?;
5402 let mut parent_columns: Vec<String> = Vec::new();
5403 if matches!(self.peek(), Token::LParen) {
5404 self.advance();
5405 loop {
5406 parent_columns.push(self.expect_ident_like()?);
5407 match self.peek() {
5408 Token::Comma => {
5409 self.advance();
5410 }
5411 Token::RParen => {
5412 self.advance();
5413 break;
5414 }
5415 other => {
5416 return Err(self.err(format!(
5417 "expected ',' or ')' in REFERENCES column list, got {other:?}"
5418 )));
5419 }
5420 }
5421 }
5422 }
5423 if !parent_columns.is_empty() && parent_columns.len() != expected_arity {
5424 return Err(self.err(format!(
5425 "FK arity mismatch: {} local column(s) vs {} parent column(s)",
5426 expected_arity,
5427 parent_columns.len()
5428 )));
5429 }
5430 let mut on_delete = FkAction::Restrict;
5443 let mut on_update = FkAction::Restrict;
5444 let mut seen_on_delete = false;
5445 let mut seen_on_update = false;
5446 loop {
5447 let before = self.pos;
5449 self.consume_optional_deferrable_clauses()?;
5450 if self.pos != before {
5451 continue;
5452 }
5453 if !matches!(self.peek(), Token::On) {
5455 break;
5456 }
5457 self.advance();
5458 let which = self.advance();
5459 let action = self.parse_fk_action()?;
5460 match which {
5461 Token::Ident(ref s) if s.eq_ignore_ascii_case("delete") => {
5462 if seen_on_delete {
5463 return Err(self.err("ON DELETE specified twice".into()));
5464 }
5465 seen_on_delete = true;
5466 on_delete = action;
5467 }
5468 Token::Ident(ref s) if s.eq_ignore_ascii_case("update") => {
5469 if seen_on_update {
5470 return Err(self.err("ON UPDATE specified twice".into()));
5471 }
5472 seen_on_update = true;
5473 on_update = action;
5474 }
5475 other => {
5476 return Err(
5477 self.err(format!("expected DELETE or UPDATE after ON, got {other:?}"))
5478 );
5479 }
5480 }
5481 }
5482 Ok((parent_table, parent_columns, on_delete, on_update))
5483 }
5484
5485 fn parse_fk_action(&mut self) -> Result<FkAction, ParseError> {
5488 match self.advance() {
5489 Token::Ident(s) if s.eq_ignore_ascii_case("cascade") => Ok(FkAction::Cascade),
5490 Token::Ident(s) if s.eq_ignore_ascii_case("restrict") => Ok(FkAction::Restrict),
5491 Token::Ident(s) if s.eq_ignore_ascii_case("set") => match self.advance() {
5492 Token::Null => Ok(FkAction::SetNull),
5493 Token::Default => Ok(FkAction::SetDefault),
5494 other => Err(self.err(format!(
5495 "expected NULL or DEFAULT after SET in FK action, got {other:?}"
5496 ))),
5497 },
5498 Token::Ident(s) if s.eq_ignore_ascii_case("no") => match self.advance() {
5499 Token::Ident(s) if s.eq_ignore_ascii_case("action") => Ok(FkAction::NoAction),
5500 other => Err(self.err(format!(
5501 "expected ACTION after NO in FK action, got {other:?}"
5502 ))),
5503 },
5504 other => Err(self.err(format!(
5505 "expected CASCADE | RESTRICT | SET NULL | SET DEFAULT | NO ACTION, got {other:?}"
5506 ))),
5507 }
5508 }
5509
5510 fn consume_if_not_exists(&mut self) -> bool {
5513 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5517 if !looks_like_if {
5518 return false;
5519 }
5520 if !matches!(self.tokens.get(self.pos + 1), Some(Token::Not)) {
5523 return false;
5524 }
5525 if !matches!(
5526 self.tokens.get(self.pos + 2),
5527 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5528 ) {
5529 return false;
5530 }
5531 self.advance(); self.advance(); self.advance(); true
5535 }
5536
5537 fn consume_if_exists(&mut self) -> bool {
5541 let looks_like_if = matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("if"));
5542 if !looks_like_if {
5543 return false;
5544 }
5545 if !matches!(
5546 self.tokens.get(self.pos + 1),
5547 Some(Token::Ident(s)) if s.eq_ignore_ascii_case("exists")
5548 ) {
5549 return false;
5550 }
5551 self.advance(); self.advance(); true
5554 }
5555
5556 fn consume_optional_index_column_qualifiers(&mut self) {
5562 loop {
5563 match self.peek() {
5564 Token::Asc | Token::Desc => {
5565 self.advance();
5566 }
5567 Token::Ident(s) if s.eq_ignore_ascii_case("nulls") => {
5568 let look = self.tokens.get(self.pos + 1);
5569 if matches!(
5570 look,
5571 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("first")
5572 || k.eq_ignore_ascii_case("last")
5573 ) {
5574 self.advance();
5575 self.advance();
5576 } else {
5577 break;
5578 }
5579 }
5580 _ => break,
5581 }
5582 }
5583 }
5584
5585 fn parse_create_index_stmt_after_create(
5586 &mut self,
5587 is_unique: bool,
5588 ) -> Result<Statement, ParseError> {
5589 debug_assert!(matches!(self.peek(), Token::Index));
5591 self.advance();
5592 let if_not_exists = self.consume_if_not_exists();
5593 let name = self.expect_ident_like()?;
5594 if !matches!(self.peek(), Token::On) {
5595 return Err(self.err(format!(
5596 "expected ON after CREATE INDEX <name>, got {:?}",
5597 self.peek()
5598 )));
5599 }
5600 self.advance();
5601 let table = self.expect_ident_like()?;
5602 let method = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
5607 self.advance();
5608 let m = self.expect_ident_like()?;
5609 match m.to_ascii_lowercase().as_str() {
5610 "hnsw" => IndexMethod::Hnsw,
5611 "btree" => IndexMethod::BTree,
5612 "brin" => IndexMethod::Brin,
5613 "gin" => IndexMethod::Gin,
5618 "gist" | "spgist" | "hash" => IndexMethod::BTree,
5627 "ivfflat" => IndexMethod::Hnsw,
5635 other => {
5636 return Err(self.err(alloc::format!(
5637 "unknown index method {other:?}; supported: hnsw, btree, brin, gin (gist/spgist/hash accepted as BTree fallback)"
5638 )));
5639 }
5640 }
5641 } else {
5642 IndexMethod::BTree
5643 };
5644 if !matches!(self.peek(), Token::LParen) {
5645 return Err(self.err(format!(
5646 "expected '(' before indexed column, got {:?}",
5647 self.peek()
5648 )));
5649 }
5650 self.advance();
5651 let mut opclass: Option<String> = None;
5660 let (column, expression): (String, Option<Expr>) = match self.peek().clone() {
5661 Token::Ident(s) | Token::QuotedIdent(s)
5668 if matches!(
5669 self.tokens.get(self.pos + 1),
5670 Some(Token::RParen | Token::Comma)
5671 ) =>
5672 {
5673 self.advance();
5674 (s, None)
5675 }
5676 Token::Ident(s) | Token::QuotedIdent(s)
5690 if matches!(
5691 self.tokens.get(self.pos + 1),
5692 Some(Token::Ident(_) | Token::QuotedIdent(_))
5693 ) && matches!(self.tokens.get(self.pos + 2), Some(Token::Dot))
5694 && matches!(
5695 self.tokens.get(self.pos + 3),
5696 Some(Token::Ident(op) | Token::QuotedIdent(op))
5697 if is_vector_opclass_name(op)
5698 ) =>
5699 {
5700 self.advance(); self.advance(); self.advance(); let op_tok = self.advance();
5704 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5705 opclass = Some(op.to_ascii_lowercase());
5706 }
5707 (s, None)
5708 }
5709 Token::Ident(s) | Token::QuotedIdent(s)
5710 if matches!(
5711 self.tokens.get(self.pos + 1),
5712 Some(Token::Ident(op) | Token::QuotedIdent(op))
5713 if is_vector_opclass_name(op)
5714 ) =>
5715 {
5716 self.advance(); let op_tok = self.advance();
5720 if let Token::Ident(op) | Token::QuotedIdent(op) = op_tok {
5721 opclass = Some(op.to_ascii_lowercase());
5722 }
5723 (s, None)
5724 }
5725 Token::Ident(_) | Token::QuotedIdent(_) => {
5726 let key_expr = self.parse_expr(0)?;
5727 let primary = extract_first_column(&key_expr).ok_or_else(|| {
5728 self.err("expression index key must reference at least one column".into())
5729 })?;
5730 (primary, Some(key_expr))
5731 }
5732 other => {
5733 return Err(self.err(format!(
5734 "expected column ident or expression, got {other:?}"
5735 )));
5736 }
5737 };
5738 let mut extra_columns: Vec<String> = Vec::new();
5747 self.consume_optional_index_column_qualifiers();
5749 while matches!(self.peek(), Token::Comma) {
5750 self.advance();
5751 let extra = self.expect_ident_like()?;
5752 self.consume_optional_index_column_qualifiers();
5753 extra_columns.push(extra);
5754 }
5755 if !matches!(self.peek(), Token::RParen) {
5756 return Err(self.err(format!(
5757 "expected ')' after indexed column / expression, got {:?}",
5758 self.peek()
5759 )));
5760 }
5761 self.advance();
5762 let included_columns = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("include"))
5766 {
5767 self.advance();
5768 if !matches!(self.peek(), Token::LParen) {
5769 return Err(self.err(format!("expected '(' after INCLUDE, got {:?}", self.peek())));
5770 }
5771 self.advance();
5772 let mut cols = Vec::new();
5773 loop {
5774 cols.push(self.expect_ident_like()?);
5775 match self.peek() {
5776 Token::Comma => {
5777 self.advance();
5778 }
5779 Token::RParen => {
5780 self.advance();
5781 break;
5782 }
5783 other => {
5784 return Err(self.err(format!(
5785 "expected ',' or ')' in INCLUDE list, got {other:?}"
5786 )));
5787 }
5788 }
5789 }
5790 cols
5791 } else {
5792 Vec::new()
5793 };
5794 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with")) {
5800 self.advance();
5801 if !matches!(self.peek(), Token::LParen) {
5802 return Err(self.err(format!(
5803 "expected '(' after WITH in CREATE INDEX, got {:?}",
5804 self.peek()
5805 )));
5806 }
5807 self.advance();
5808 loop {
5809 if matches!(self.peek(), Token::RParen) {
5810 self.advance();
5811 break;
5812 }
5813 let _ = self.advance(); if matches!(self.peek(), Token::Eq) {
5816 self.advance();
5817 let _ = self.advance(); }
5819 match self.peek() {
5820 Token::Comma => {
5821 self.advance();
5822 }
5823 Token::RParen => {
5824 self.advance();
5825 break;
5826 }
5827 other => {
5828 return Err(self.err(format!(
5829 "expected ',' or ')' in WITH (…) clause, got {other:?}"
5830 )));
5831 }
5832 }
5833 }
5834 }
5835 let partial_predicate = if matches!(self.peek(), Token::Where) {
5837 self.advance();
5838 Some(self.parse_expr(0)?)
5839 } else {
5840 None
5841 };
5842 if is_unique && !matches!(method, IndexMethod::BTree) {
5847 return Err(self.err(alloc::format!(
5848 "UNIQUE is only supported on BTree indexes, got USING {:?}",
5849 method
5850 )));
5851 }
5852 Ok(Statement::CreateIndex(CreateIndexStatement {
5853 name,
5854 table,
5855 column,
5856 method,
5857 if_not_exists,
5858 included_columns,
5859 partial_predicate,
5860 extra_columns: extra_columns.clone(),
5861 expression,
5862 is_unique,
5863 opclass,
5864 }))
5865 }
5866
5867 fn parse_column_def_with_fk(
5872 &mut self,
5873 ) -> Result<(ColumnDef, Option<ForeignKeyConstraint>), ParseError> {
5874 let col = self.parse_column_def()?;
5875 let inline_references = matches!(
5877 self.peek(),
5878 Token::Ident(s) if s.eq_ignore_ascii_case("references")
5879 );
5880 if !inline_references {
5881 return Ok((col, None));
5882 }
5883 let (parent_table, parent_columns, on_delete, on_update) = self.parse_references_tail(1)?;
5884 let fk = ForeignKeyConstraint {
5885 name: None,
5886 columns: vec![col.name.clone()],
5887 parent_table,
5888 parent_columns,
5889 on_delete,
5890 on_update,
5891 };
5892 Ok((col, Some(fk)))
5893 }
5894
5895 fn parse_column_type_name(&mut self) -> Result<ColumnTypeName, ParseError> {
5903 let (ty, _, _, _, _, _, _, _) = self.parse_type_with_implied_flags()?;
5904 Ok(ty)
5905 }
5906
5907 #[allow(clippy::type_complexity)]
5908 fn parse_type_with_implied_flags(
5909 &mut self,
5910 ) -> Result<
5911 (
5912 ColumnTypeName,
5913 bool,
5914 bool,
5915 Option<String>,
5916 Collation,
5917 bool,
5918 Option<Vec<String>>,
5922 Option<Vec<String>>,
5925 ),
5926 ParseError,
5927 > {
5928 let mut ty_ident = match self.advance() {
5929 Token::Ident(s) => s,
5930 other => {
5931 return Err(ParseError {
5932 message: format!("expected column type, got {other:?}"),
5933 token_pos: self.pos.saturating_sub(1),
5934 });
5935 }
5936 };
5937 while matches!(self.peek(), Token::Dot) {
5942 self.advance();
5943 ty_ident = self.expect_ident_like()?;
5944 }
5945 let mut implied_auto_increment = false;
5946 let mut implied_not_null = false;
5947 let mut user_type_ref: Option<String> = None;
5948 let mut inline_enum_variants: Option<Vec<String>> = None;
5953 let mut inline_set_variants: Option<Vec<String>> = None;
5955 let mut ty = match ty_ident.as_str() {
5956 "smallserial" | "serial2" => {
5958 implied_auto_increment = true;
5959 implied_not_null = true;
5960 ColumnTypeName::SmallInt
5961 }
5962 "serial" | "serial4" => {
5963 implied_auto_increment = true;
5964 implied_not_null = true;
5965 ColumnTypeName::Int
5966 }
5967 "bigserial" | "serial8" => {
5968 implied_auto_increment = true;
5969 implied_not_null = true;
5970 ColumnTypeName::BigInt
5971 }
5972 "smallint" => {
5978 self.consume_optional_paren_size();
5983 ColumnTypeName::SmallInt
5984 }
5985 "tinyint" => {
5996 let width = self.peek_optional_paren_size_value();
5997 self.consume_optional_paren_size();
5998 if width == Some(1) {
5999 ColumnTypeName::Bool
6000 } else {
6001 ColumnTypeName::SmallInt
6002 }
6003 }
6004 "int" | "integer" | "mediumint" => {
6005 self.consume_optional_paren_size();
6006 ColumnTypeName::Int
6007 }
6008 "bigint" => {
6009 self.consume_optional_paren_size();
6010 ColumnTypeName::BigInt
6011 }
6012 "float" | "double" | "real" => {
6017 if ty_ident.eq_ignore_ascii_case("double")
6018 && matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("precision"))
6019 {
6020 self.advance();
6021 }
6022 ColumnTypeName::Float
6023 }
6024 "float4" | "float8" => ColumnTypeName::Float,
6026 "text" => ColumnTypeName::Text,
6027 "bool" | "boolean" => ColumnTypeName::Bool,
6028 "varchar" => ColumnTypeName::Varchar(self.parse_paren_size("VARCHAR")?),
6029 "char" => ColumnTypeName::Char(self.parse_paren_size("CHAR")?),
6030 "vector" => {
6031 let dim = self.parse_paren_size("VECTOR")?;
6032 let encoding = self.parse_optional_vector_encoding()?;
6033 ColumnTypeName::Vector { dim, encoding }
6034 }
6035 "numeric" => {
6036 let (precision, scale) = self.parse_optional_numeric_params()?;
6037 ColumnTypeName::Numeric(precision, scale)
6038 }
6039 "date" => ColumnTypeName::Date,
6040 "timestamp" | "datetime" => {
6043 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("with"))
6049 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6050 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6051 {
6052 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamptz
6056 } else if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("without"))
6057 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("time"))
6058 && matches!(self.tokens.get(self.pos + 2), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("zone"))
6059 {
6060 self.advance(); self.advance(); self.advance(); ColumnTypeName::Timestamp
6064 } else {
6065 self.consume_optional_paren_size();
6069 ColumnTypeName::Timestamp
6070 }
6071 }
6072 "timestamptz" => ColumnTypeName::Timestamptz,
6076 "json" => ColumnTypeName::Json,
6081 "jsonb" => ColumnTypeName::Jsonb,
6082 "bytea" | "bytes" => ColumnTypeName::Bytes,
6088 "inet" | "cidr" | "macaddr" => ColumnTypeName::Text,
6098 "tsvector" => ColumnTypeName::TsVector,
6103 "tsquery" => ColumnTypeName::TsQuery,
6104 "uuid" => ColumnTypeName::Uuid,
6108 "time" => ColumnTypeName::Time,
6111 "year" => ColumnTypeName::Year,
6114 "timetz" => ColumnTypeName::TimeTz,
6117 "money" => ColumnTypeName::Money,
6120 "int4range" => ColumnTypeName::Range(RangeKindAst::Int4),
6122 "int8range" => ColumnTypeName::Range(RangeKindAst::Int8),
6123 "numrange" => ColumnTypeName::Range(RangeKindAst::Num),
6124 "tsrange" => ColumnTypeName::Range(RangeKindAst::Ts),
6125 "tstzrange" => ColumnTypeName::Range(RangeKindAst::TsTz),
6126 "daterange" => ColumnTypeName::Range(RangeKindAst::Date),
6127 "hstore" => ColumnTypeName::Hstore,
6129 "enum" => {
6135 if !matches!(self.peek(), Token::LParen) {
6137 return Err(self.err(alloc::format!(
6138 "expected '(' after ENUM, got {:?}",
6139 self.peek()
6140 )));
6141 }
6142 self.advance();
6143 let mut variants: Vec<String> = Vec::new();
6144 loop {
6145 match self.advance() {
6146 Token::String(s) => variants.push(s),
6147 other => {
6148 return Err(self.err(alloc::format!(
6149 "ENUM(...) expects string literal variants, got {other:?}"
6150 )));
6151 }
6152 }
6153 match self.peek() {
6154 Token::Comma => {
6155 self.advance();
6156 continue;
6157 }
6158 Token::RParen => {
6159 self.advance();
6160 break;
6161 }
6162 other => {
6163 return Err(self.err(alloc::format!(
6164 "expected ',' or ')' in ENUM(...), got {other:?}"
6165 )));
6166 }
6167 }
6168 }
6169 if variants.is_empty() {
6170 return Err(self.err("ENUM(...) must declare at least one variant".into()));
6171 }
6172 inline_enum_variants = Some(variants);
6173 ColumnTypeName::Text
6176 }
6177 "set" => {
6181 if !matches!(self.peek(), Token::LParen) {
6182 return Err(self.err(alloc::format!(
6183 "expected '(' after SET, got {:?}",
6184 self.peek()
6185 )));
6186 }
6187 self.advance();
6188 let mut variants: Vec<String> = Vec::new();
6189 loop {
6190 match self.advance() {
6191 Token::String(s) => variants.push(s),
6192 other => {
6193 return Err(self.err(alloc::format!(
6194 "SET(...) expects string literal variants, got {other:?}"
6195 )));
6196 }
6197 }
6198 match self.peek() {
6199 Token::Comma => {
6200 self.advance();
6201 continue;
6202 }
6203 Token::RParen => {
6204 self.advance();
6205 break;
6206 }
6207 other => {
6208 return Err(self.err(alloc::format!(
6209 "expected ',' or ')' in SET(...), got {other:?}"
6210 )));
6211 }
6212 }
6213 }
6214 if variants.is_empty() {
6215 return Err(self.err("SET(...) must declare at least one variant".into()));
6216 }
6217 inline_set_variants = Some(variants);
6218 ColumnTypeName::Text
6219 }
6220 _other => {
6221 user_type_ref = Some(ty_ident.clone());
6227 ColumnTypeName::Text
6228 }
6229 };
6230 let is_unsigned = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("unsigned"))
6240 {
6241 self.advance();
6242 true
6243 } else {
6244 false
6245 };
6246 let mut collation = Collation::Binary;
6262 loop {
6263 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("character"))
6264 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("set"))
6265 {
6266 self.advance(); self.advance(); if matches!(
6269 self.peek(),
6270 Token::Ident(_) | Token::QuotedIdent(_) | Token::String(_)
6271 ) {
6272 self.advance();
6273 }
6274 continue;
6275 }
6276 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("collate")) {
6277 self.advance(); let read_collation_atom = |this: &mut Self| -> Option<alloc::string::String> {
6284 match this.peek().clone() {
6285 Token::Ident(s) | Token::QuotedIdent(s) | Token::String(s) => {
6286 this.advance();
6287 Some(s)
6288 }
6289 Token::Default => {
6290 this.advance();
6291 Some(alloc::string::String::from("default"))
6292 }
6293 _ => None,
6294 }
6295 };
6296 let raw = if let Some(head) = read_collation_atom(self) {
6297 if matches!(self.peek(), Token::Dot) {
6299 self.advance();
6300 let tail = read_collation_atom(self).unwrap_or_default();
6301 alloc::format!("{head}.{tail}")
6302 } else {
6303 head
6304 }
6305 } else {
6306 alloc::string::String::new()
6307 };
6308 if !raw.is_empty() {
6309 let parsed = Collation::from_collation_name(&raw);
6310 if parsed != Collation::Binary {
6316 collation = parsed;
6317 }
6318 }
6319 continue;
6320 }
6321 break;
6322 }
6323 if matches!(self.peek(), Token::LBracket) {
6328 self.advance();
6329 if !matches!(self.peek(), Token::RBracket) {
6330 return Err(self.err(alloc::format!(
6331 "TEXT[] takes no dimension; got {:?}",
6332 self.peek()
6333 )));
6334 }
6335 self.advance();
6336 ty = match ty {
6340 ColumnTypeName::Text => ColumnTypeName::TextArray,
6341 ColumnTypeName::Int => ColumnTypeName::IntArray,
6342 ColumnTypeName::BigInt => ColumnTypeName::BigIntArray,
6343 other => {
6344 return Err(self.err(alloc::format!(
6345 "v7.11 supports TEXT[] / INT[] / BIGINT[] only; got {other:?}[]"
6346 )));
6347 }
6348 };
6349 if matches!(self.peek(), Token::LBracket) {
6352 self.advance();
6353 if !matches!(self.peek(), Token::RBracket) {
6354 return Err(self.err(alloc::format!(
6355 "TYPE[][] second dimension takes no size; got {:?}",
6356 self.peek()
6357 )));
6358 }
6359 self.advance();
6360 ty = match ty {
6361 ColumnTypeName::IntArray => ColumnTypeName::IntArray2D,
6362 ColumnTypeName::BigIntArray => ColumnTypeName::BigIntArray2D,
6363 ColumnTypeName::TextArray => ColumnTypeName::TextArray2D,
6364 other => {
6365 return Err(self.err(alloc::format!(
6366 "v7.17 2D arrays support INT[][] / BIGINT[][] / \
6367 TEXT[][] only; got {other:?}"
6368 )));
6369 }
6370 };
6371 }
6372 }
6373 Ok((
6374 ty,
6375 implied_auto_increment,
6376 implied_not_null,
6377 user_type_ref,
6378 collation,
6379 is_unsigned,
6380 inline_enum_variants,
6381 inline_set_variants,
6382 ))
6383 }
6384
6385 fn parse_column_def(&mut self) -> Result<ColumnDef, ParseError> {
6386 if let Token::Ident(s) = self.peek()
6395 && [
6396 "unique",
6397 "primary",
6398 "foreign",
6399 "constraint",
6400 "check",
6401 "references",
6402 "exclude",
6403 ]
6404 .iter()
6405 .any(|kw| s.eq_ignore_ascii_case(kw))
6406 {
6407 return Err(self.err(alloc::format!(
6408 "unexpected reserved keyword '{s}' at start of column definition \
6409 (malformed table constraint?)"
6410 )));
6411 }
6412 let name = self.expect_ident_like()?;
6413 let (
6414 ty,
6415 implied_auto_increment,
6416 implied_not_null,
6417 user_type_ref,
6418 collation,
6419 is_unsigned,
6420 inline_enum_variants,
6421 inline_set_variants,
6422 ) = self.parse_type_with_implied_flags()?;
6423 let mut default: Option<Expr> = None;
6427 let mut nullable = !implied_not_null;
6428 let mut nullability_seen = implied_not_null;
6429 let mut auto_increment = implied_auto_increment;
6430 let mut is_primary_key = false;
6431 let mut is_unique = false;
6432 let mut check: Option<Expr> = None;
6433 let mut on_update_runtime: Option<Expr> = None;
6434 loop {
6435 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("constraint")) {
6441 self.advance();
6442 let _name = self.expect_ident_like()?;
6443 continue;
6444 }
6445 if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("generated")) {
6455 self.advance();
6456 match self.peek().clone() {
6457 Token::Ident(s) if s.eq_ignore_ascii_case("always") => {
6458 self.advance();
6459 }
6460 Token::By => {
6462 self.advance();
6463 if !matches!(self.peek(), Token::Default) {
6464 return Err(self.err(alloc::format!(
6465 "expected DEFAULT after GENERATED BY, got {:?}",
6466 self.peek()
6467 )));
6468 }
6469 self.advance();
6470 }
6471 other => {
6472 return Err(self.err(alloc::format!(
6473 "expected ALWAYS or BY DEFAULT after GENERATED, got {other:?}"
6474 )));
6475 }
6476 }
6477 if !matches!(self.peek(), Token::As) {
6478 return Err(self.err(alloc::format!(
6479 "expected AS after GENERATED ALWAYS/BY DEFAULT, got {:?}",
6480 self.peek()
6481 )));
6482 }
6483 self.advance();
6484 if matches!(self.peek(), Token::LParen) {
6485 return Err(self.err(
6486 "generated expression columns (GENERATED … AS (expr) STORED) \
6487 are not supported"
6488 .into(),
6489 ));
6490 }
6491 self.expect_keyword_ident("identity")?;
6492 if matches!(self.peek(), Token::LParen) {
6496 let mut depth = 0usize;
6497 loop {
6498 match self.advance() {
6499 Token::LParen => depth += 1,
6500 Token::RParen => {
6501 depth -= 1;
6502 if depth == 0 {
6503 break;
6504 }
6505 }
6506 Token::Eof => {
6507 return Err(self.err(
6508 "unterminated sequence-options parens after IDENTITY".into(),
6509 ));
6510 }
6511 _ => {}
6512 }
6513 }
6514 }
6515 auto_increment = true;
6516 nullable = false;
6518 continue;
6519 }
6520 if matches!(self.peek(), Token::On)
6525 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s)) if s.eq_ignore_ascii_case("update"))
6526 {
6527 self.advance(); self.advance(); let next = self.peek().clone();
6531 match next {
6532 Token::Ident(s) | Token::QuotedIdent(s)
6533 if s.eq_ignore_ascii_case("current_timestamp") =>
6534 {
6535 self.advance();
6536 if matches!(self.peek(), Token::LParen) {
6538 self.advance();
6539 if !matches!(self.peek(), Token::Integer(_)) {
6540 return Err(self.err(alloc::format!(
6541 "expected integer precision inside CURRENT_TIMESTAMP(…), got {:?}",
6542 self.peek()
6543 )));
6544 }
6545 self.advance();
6546 if !matches!(self.peek(), Token::RParen) {
6547 return Err(self.err(alloc::format!(
6548 "expected ')' after CURRENT_TIMESTAMP precision, got {:?}",
6549 self.peek()
6550 )));
6551 }
6552 self.advance();
6553 }
6554 on_update_runtime = Some(Expr::FunctionCall {
6555 name: "now".into(),
6556 args: Vec::new(),
6557 });
6558 continue;
6559 }
6560 other => {
6561 return Err(self.err(alloc::format!(
6562 "v7.17 only supports ON UPDATE CURRENT_TIMESTAMP, got {other:?}"
6563 )));
6564 }
6565 }
6566 }
6567 if matches!(self.peek(), Token::Default) {
6568 if default.is_some() {
6569 return Err(self.err("DEFAULT specified twice".into()));
6570 }
6571 self.advance();
6572 default = Some(self.parse_expr(0)?);
6573 continue;
6574 }
6575 if matches!(self.peek(), Token::Not) {
6576 if nullability_seen {
6577 return Err(self.err("NOT NULL specified twice".into()));
6578 }
6579 self.advance();
6580 if !matches!(self.peek(), Token::Null) {
6581 return Err(self.err(format!(
6582 "expected NULL after NOT in column def, got {:?}",
6583 self.peek()
6584 )));
6585 }
6586 self.advance();
6587 nullable = false;
6588 nullability_seen = true;
6589 continue;
6590 }
6591 if matches!(self.peek(), Token::Null) {
6597 if nullability_seen && !nullable {
6598 return Err(self.err("column declared NOT NULL then NULL — pick one".into()));
6599 }
6600 self.advance();
6601 nullable = true;
6602 nullability_seen = true;
6603 continue;
6604 }
6605 if let Token::Ident(s) = self.peek()
6608 && (s.eq_ignore_ascii_case("auto_increment")
6609 || s.eq_ignore_ascii_case("autoincrement"))
6610 {
6611 if auto_increment {
6612 return Err(self.err("AUTO_INCREMENT specified twice".into()));
6613 }
6614 self.advance();
6615 auto_increment = true;
6616 continue;
6617 }
6618 if let Token::Ident(s) = self.peek()
6623 && s.eq_ignore_ascii_case("primary")
6624 {
6625 if is_primary_key {
6626 return Err(self.err("PRIMARY KEY specified twice".into()));
6627 }
6628 let next = self.tokens.get(self.pos + 1);
6630 let next_is_key = matches!(
6631 next,
6632 Some(Token::Ident(k)) if k.eq_ignore_ascii_case("key")
6633 );
6634 if !next_is_key {
6635 return Err(self.err(format!(
6636 "expected KEY after PRIMARY in column def, got {:?}",
6637 next
6638 )));
6639 }
6640 self.advance(); self.advance(); is_primary_key = true;
6643 if nullability_seen && nullable {
6644 return Err(self.err(
6645 "column declared NULL but inline PRIMARY KEY implies NOT NULL".into(),
6646 ));
6647 }
6648 nullable = false;
6649 nullability_seen = true;
6650 continue;
6651 }
6652 if let Token::Ident(s) = self.peek()
6656 && s.eq_ignore_ascii_case("unique")
6657 {
6658 if is_unique {
6659 return Err(self.err("UNIQUE specified twice".into()));
6660 }
6661 self.advance();
6662 is_unique = true;
6663 continue;
6664 }
6665 if let Token::Ident(s) = self.peek()
6670 && s.eq_ignore_ascii_case("check")
6671 {
6672 self.advance();
6673 if !matches!(self.peek(), Token::LParen) {
6674 return Err(self.err(alloc::format!(
6675 "expected '(' after CHECK in column def, got {:?}",
6676 self.peek()
6677 )));
6678 }
6679 self.advance();
6680 let pred = self.parse_expr(0)?;
6681 if !matches!(self.peek(), Token::RParen) {
6682 return Err(self.err(alloc::format!(
6683 "expected ')' to close CHECK predicate, got {:?}",
6684 self.peek()
6685 )));
6686 }
6687 self.advance();
6688 check = Some(match check.take() {
6689 Some(prev) => Expr::Binary {
6690 op: BinOp::And,
6691 lhs: Box::new(prev),
6692 rhs: Box::new(pred),
6693 },
6694 None => pred,
6695 });
6696 continue;
6697 }
6698 break;
6699 }
6700 Ok(ColumnDef {
6701 name,
6702 ty,
6703 nullable,
6704 default,
6705 auto_increment,
6706 is_primary_key,
6707 is_unique,
6708 check,
6709 user_type_ref,
6710 on_update_runtime,
6711 collation,
6712 is_unsigned,
6713 inline_enum_variants,
6714 inline_set_variants,
6715 })
6716 }
6717
6718 fn parse_optional_numeric_params(&mut self) -> Result<(u8, u8), ParseError> {
6722 if !matches!(self.peek(), Token::LParen) {
6723 return Ok((0, 0));
6727 }
6728 self.advance();
6729 let precision = match self.advance() {
6730 Token::Integer(n) if (1..=38).contains(&n) => u8::try_from(n).expect("range-checked"),
6731 other => {
6732 return Err(ParseError {
6733 message: format!(
6734 "NUMERIC precision must be an integer in 1..=38, got {other:?}"
6735 ),
6736 token_pos: self.pos.saturating_sub(1),
6737 });
6738 }
6739 };
6740 let scale = if matches!(self.peek(), Token::Comma) {
6741 self.advance();
6742 match self.advance() {
6743 Token::Integer(n) if (0..=i64::from(precision)).contains(&n) => {
6744 u8::try_from(n).expect("range-checked")
6745 }
6746 other => {
6747 return Err(ParseError {
6748 message: format!(
6749 "NUMERIC scale must be a non-negative integer ≤ precision, got {other:?}"
6750 ),
6751 token_pos: self.pos.saturating_sub(1),
6752 });
6753 }
6754 }
6755 } else {
6756 0
6757 };
6758 if !matches!(self.peek(), Token::RParen) {
6759 return Err(self.err(format!(
6760 "expected ')' to close NUMERIC params, got {:?}",
6761 self.peek()
6762 )));
6763 }
6764 self.advance();
6765 Ok((precision, scale))
6766 }
6767
6768 fn parse_optional_vector_encoding(&mut self) -> Result<VecEncoding, ParseError> {
6776 if !matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("using")) {
6777 return Ok(VecEncoding::F32);
6778 }
6779 let n1 = self.tokens.get(self.pos + 1);
6785 let next_is_encoding = matches!(
6786 n1,
6787 Some(Token::Ident(s))
6788 if s.eq_ignore_ascii_case("sq8") || s.eq_ignore_ascii_case("half")
6789 );
6790 if !next_is_encoding {
6791 return Ok(VecEncoding::F32);
6792 }
6793 self.advance();
6794 let enc_ident = match self.advance() {
6795 Token::Ident(s) => s,
6796 other => {
6797 return Err(self.err(format!(
6798 "expected vector encoding after USING, got {other:?}"
6799 )));
6800 }
6801 };
6802 match enc_ident.to_ascii_lowercase().as_str() {
6803 "sq8" => Ok(VecEncoding::Sq8),
6804 "half" => Ok(VecEncoding::F16),
6807 other => Err(self.err(format!(
6808 "unknown vector encoding {other:?}; supported: SQ8, HALF"
6809 ))),
6810 }
6811 }
6812
6813 fn peek_optional_paren_size_value(&self) -> Option<i64> {
6819 if !matches!(self.peek(), Token::LParen) {
6820 return None;
6821 }
6822 let next = self.tokens.get(self.pos + 1)?;
6823 let n = match next {
6824 Token::Integer(n) => *n,
6825 _ => return None,
6826 };
6827 if !matches!(self.tokens.get(self.pos + 2), Some(Token::RParen)) {
6828 return None;
6829 }
6830 Some(n)
6831 }
6832
6833 fn consume_optional_paren_size(&mut self) {
6837 if !matches!(self.peek(), Token::LParen) {
6838 return;
6839 }
6840 self.advance();
6841 let mut depth = 1usize;
6843 while depth > 0 {
6844 match self.peek() {
6845 Token::LParen => depth += 1,
6846 Token::RParen => depth -= 1,
6847 Token::Eof => return,
6848 _ => {}
6849 }
6850 self.advance();
6851 }
6852 }
6853
6854 fn parse_paren_size(&mut self, label: &str) -> Result<u32, ParseError> {
6855 if !matches!(self.peek(), Token::LParen) {
6856 return Err(self.err(format!("{label} type requires (N), got {:?}", self.peek())));
6857 }
6858 self.advance();
6859 let n = match self.advance() {
6860 Token::Integer(n) if n > 0 => u32::try_from(n).map_err(|_| ParseError {
6861 message: format!("{label} size too large: {n}"),
6862 token_pos: self.pos.saturating_sub(1),
6863 })?,
6864 other => {
6865 return Err(ParseError {
6866 message: format!("expected positive integer {label} size, got {other:?}"),
6867 token_pos: self.pos.saturating_sub(1),
6868 });
6869 }
6870 };
6871 if !matches!(self.peek(), Token::RParen) {
6872 return Err(self.err(format!(
6873 "expected ')' after {label} size, got {:?}",
6874 self.peek()
6875 )));
6876 }
6877 self.advance();
6878 Ok(n)
6879 }
6880
6881 fn parse_insert_stmt(&mut self) -> Result<Statement, ParseError> {
6882 debug_assert!(matches!(self.peek(), Token::Insert));
6883 self.advance();
6884 if !matches!(self.peek(), Token::Into) {
6885 return Err(self.err(format!("expected INTO after INSERT, got {:?}", self.peek())));
6886 }
6887 self.advance();
6888 let table = self.expect_ident_like()?;
6889 let columns = if matches!(self.peek(), Token::LParen) {
6891 self.advance();
6892 let mut names = Vec::new();
6893 loop {
6894 names.push(self.expect_ident_like()?);
6895 match self.peek() {
6896 Token::Comma => {
6897 self.advance();
6898 }
6899 Token::RParen => {
6900 self.advance();
6901 break;
6902 }
6903 other => {
6904 return Err(self.err(format!(
6905 "expected ',' or ')' in INSERT column list, got {other:?}"
6906 )));
6907 }
6908 }
6909 }
6910 Some(names)
6911 } else {
6912 None
6913 };
6914 if matches!(self.peek(), Token::Select) {
6917 let select_stmt = match self.parse_select_stmt()? {
6918 Statement::Select(s) => s,
6919 other => {
6920 return Err(self.err(alloc::format!(
6921 "expected SELECT after INSERT INTO ... target, got {other:?}"
6922 )));
6923 }
6924 };
6925 let on_conflict = self.parse_optional_on_conflict()?;
6926 let returning = self.parse_optional_returning()?;
6927 return Ok(Statement::Insert(InsertStatement {
6928 table,
6929 columns,
6930 rows: Vec::new(),
6931 select_source: Some(Box::new(select_stmt)),
6932 on_conflict,
6933 returning,
6934 }));
6935 }
6936 if !matches!(self.peek(), Token::Values) {
6937 return Err(self.err(format!(
6938 "expected VALUES or SELECT after table name, got {:?}",
6939 self.peek()
6940 )));
6941 }
6942 self.advance();
6943 if !matches!(self.peek(), Token::LParen) {
6944 return Err(self.err(format!("expected '(' after VALUES, got {:?}", self.peek())));
6945 }
6946 let mut rows = Vec::new();
6947 loop {
6948 if !matches!(self.peek(), Token::LParen) {
6950 return Err(self.err(format!(
6951 "expected '(' for next VALUES tuple, got {:?}",
6952 self.peek()
6953 )));
6954 }
6955 self.advance();
6956 let mut tuple = Vec::new();
6957 loop {
6958 tuple.push(self.parse_expr(0)?);
6959 match self.peek() {
6960 Token::Comma => {
6961 self.advance();
6962 }
6963 Token::RParen => {
6964 self.advance();
6965 break;
6966 }
6967 other => {
6968 return Err(self.err(format!(
6969 "expected ',' or ')' in VALUES tuple, got {other:?}"
6970 )));
6971 }
6972 }
6973 }
6974 if tuple.is_empty() {
6975 return Err(self.err("INSERT VALUES tuple requires at least one value".into()));
6976 }
6977 rows.push(tuple);
6978 if matches!(self.peek(), Token::Comma) {
6980 self.advance();
6981 } else {
6982 break;
6983 }
6984 }
6985 let on_conflict = self.parse_optional_on_conflict()?;
6986 let returning = self.parse_optional_returning()?;
6987 Ok(Statement::Insert(InsertStatement {
6988 table,
6989 columns,
6990 rows,
6991 select_source: None,
6992 on_conflict,
6993 returning,
6994 }))
6995 }
6996
6997 fn parse_optional_on_conflict(
7002 &mut self,
7003 ) -> Result<Option<crate::ast::OnConflictClause>, ParseError> {
7004 if !matches!(self.peek(), Token::On) {
7005 return Ok(None);
7006 }
7007 let next_is_conflict = matches!(
7010 self.tokens.get(self.pos + 1),
7011 Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("conflict")
7012 );
7013 if !next_is_conflict {
7014 return Ok(None);
7015 }
7016 self.advance(); self.advance(); let mut target_columns: Vec<String> = Vec::new();
7020 if matches!(self.peek(), Token::LParen) {
7021 self.advance();
7022 loop {
7023 target_columns.push(self.expect_ident_like()?);
7024 match self.peek() {
7025 Token::Comma => {
7026 self.advance();
7027 }
7028 Token::RParen => {
7029 self.advance();
7030 break;
7031 }
7032 other => {
7033 return Err(self.err(alloc::format!(
7034 "expected ',' or ')' in ON CONFLICT target list, got {other:?}"
7035 )));
7036 }
7037 }
7038 }
7039 }
7040 match self.advance() {
7042 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("do") => {}
7043 other => {
7044 return Err(self.err(alloc::format!(
7045 "expected DO after ON CONFLICT [(…)], got {other:?}"
7046 )));
7047 }
7048 }
7049 let action = match self.advance() {
7051 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("nothing") => {
7052 crate::ast::OnConflictAction::Nothing
7053 }
7054 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("update") => {
7055 self.parse_on_conflict_update_action()?
7056 }
7057 other => {
7058 return Err(self.err(alloc::format!(
7059 "expected NOTHING or UPDATE after ON CONFLICT DO, got {other:?}"
7060 )));
7061 }
7062 };
7063 Ok(Some(crate::ast::OnConflictClause {
7064 target_columns,
7065 action,
7066 }))
7067 }
7068
7069 fn parse_on_conflict_update_action(
7073 &mut self,
7074 ) -> Result<crate::ast::OnConflictAction, ParseError> {
7075 match self.advance() {
7077 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("set") => {}
7078 other => {
7079 return Err(self.err(alloc::format!(
7080 "expected SET after ON CONFLICT DO UPDATE, got {other:?}"
7081 )));
7082 }
7083 }
7084 let mut assignments: Vec<(String, Expr)> = Vec::new();
7085 loop {
7086 let col = self.expect_ident_like()?;
7087 if !matches!(self.peek(), Token::Eq) {
7088 return Err(self.err(alloc::format!(
7089 "expected `=` after column in ON CONFLICT DO UPDATE SET, got {:?}",
7090 self.peek()
7091 )));
7092 }
7093 self.advance();
7094 let value = self.parse_expr(0)?;
7095 assignments.push((col, value));
7096 if matches!(self.peek(), Token::Comma) {
7097 self.advance();
7098 continue;
7099 }
7100 break;
7101 }
7102 let where_ = if matches!(self.peek(), Token::Where) {
7103 self.advance();
7104 Some(self.parse_expr(0)?)
7105 } else {
7106 None
7107 };
7108 Ok(crate::ast::OnConflictAction::Update {
7109 assignments,
7110 where_,
7111 })
7112 }
7113
7114 fn parse_select_list(&mut self) -> Result<Vec<SelectItem>, ParseError> {
7115 let mut items = Vec::new();
7116 loop {
7117 items.push(self.parse_select_item()?);
7118 if matches!(self.peek(), Token::Comma) {
7119 self.advance();
7120 } else {
7121 break;
7122 }
7123 }
7124 Ok(items)
7125 }
7126
7127 fn parse_select_item(&mut self) -> Result<SelectItem, ParseError> {
7128 if matches!(self.peek(), Token::Star) {
7129 self.advance();
7130 return Ok(SelectItem::Wildcard);
7131 }
7132 let expr = self.parse_expr(0)?;
7133 let alias = self.parse_optional_alias();
7134 Ok(SelectItem::Expr { expr, alias })
7135 }
7136
7137 fn parse_table_ref(&mut self) -> Result<TableRef, ParseError> {
7138 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("lateral"))
7144 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7145 {
7146 self.advance(); self.advance(); let inner = match self.parse_one_statement()? {
7150 Statement::Select(s) => s,
7151 other => {
7152 return Err(self.err(alloc::format!(
7153 "expected SELECT inside LATERAL ( … ), got {other:?}"
7154 )));
7155 }
7156 };
7157 if !matches!(self.peek(), Token::RParen) {
7158 return Err(self.err(alloc::format!(
7159 "expected ')' after LATERAL subquery, got {:?}",
7160 self.peek()
7161 )));
7162 }
7163 self.advance();
7164 let alias_ident = self.parse_optional_alias();
7165 let name = alias_ident.clone().unwrap_or_else(|| "lateral".to_string());
7166 return Ok(TableRef {
7167 name,
7168 alias: alias_ident,
7169 as_of_segment: None,
7170 unnest_expr: None,
7171 unnest_column_aliases: Vec::new(),
7172 generate_series_args: None,
7173 lateral_subquery: Some(Box::new(inner)),
7174 });
7175 }
7176 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("unnest"))
7180 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7181 {
7182 self.advance(); self.advance(); let expr = self.parse_expr(0)?;
7185 if !matches!(self.peek(), Token::RParen) {
7186 return Err(self.err(alloc::format!(
7187 "expected ')' after unnest() argument, got {:?}",
7188 self.peek()
7189 )));
7190 }
7191 self.advance();
7192 let (alias_ident, unnest_column_aliases) = self.parse_optional_alias_with_columns();
7193 let name = alias_ident.clone().unwrap_or_else(|| "unnest".to_string());
7194 return Ok(TableRef {
7195 name,
7196 alias: alias_ident,
7197 as_of_segment: None,
7198 unnest_expr: Some(Box::new(expr)),
7199 unnest_column_aliases,
7200 generate_series_args: None,
7201 lateral_subquery: None,
7202 });
7203 }
7204 if matches!(self.peek(), Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("generate_series"))
7214 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen))
7215 {
7216 self.advance(); self.advance(); let mut args: Vec<Expr> = Vec::new();
7219 loop {
7220 args.push(self.parse_expr(0)?);
7221 if matches!(self.peek(), Token::Comma) {
7222 self.advance();
7223 continue;
7224 }
7225 break;
7226 }
7227 if !matches!(self.peek(), Token::RParen) {
7228 return Err(self.err(alloc::format!(
7229 "expected ')' after generate_series() arguments, got {:?}",
7230 self.peek()
7231 )));
7232 }
7233 self.advance();
7234 if args.len() < 2 || args.len() > 3 {
7235 return Err(self.err(alloc::format!(
7236 "generate_series() expects 2 or 3 arguments (start, stop [, step]); got {}",
7237 args.len()
7238 )));
7239 }
7240 let (alias_ident, _column_aliases) = self.parse_optional_alias_with_columns();
7241 let name = alias_ident
7242 .clone()
7243 .unwrap_or_else(|| "generate_series".to_string());
7244 return Ok(TableRef {
7245 name,
7246 alias: alias_ident,
7247 as_of_segment: None,
7248 unnest_expr: None,
7249 unnest_column_aliases: Vec::new(),
7250 generate_series_args: Some(args),
7251 lateral_subquery: None,
7252 });
7253 }
7254 let name = if let Some(synth) = self.try_peek_meta_qualified() {
7263 synth
7264 } else if let Some(synth) = self.try_peek_meta_bare() {
7265 synth
7266 } else {
7267 self.expect_ident_like()?
7268 };
7269 let as_of_segment = if matches!(self.peek(), Token::As)
7275 && matches!(self.tokens.get(self.pos + 1), Some(Token::Ident(s) | Token::QuotedIdent(s)) if s.eq_ignore_ascii_case("of"))
7276 {
7277 self.advance(); self.advance(); let kw = match self.peek().clone() {
7280 Token::Ident(s) | Token::QuotedIdent(s) => s,
7281 other => {
7282 return Err(self.err(format!("expected SEGMENT after AS OF, got {other:?}")));
7283 }
7284 };
7285 if !kw.eq_ignore_ascii_case("segment") {
7286 return Err(self.err(format!(
7287 "expected SEGMENT after AS OF, got {kw:?}; v6.10.2 supports SEGMENT only"
7288 )));
7289 }
7290 self.advance();
7291 let id = match self.advance() {
7294 Token::String(s) => s
7295 .parse::<u32>()
7296 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7297 Token::Integer(n) => u32::try_from(n)
7298 .map_err(|e| self.err(format!("AS OF SEGMENT id parse: {e}")))?,
7299 other => {
7300 return Err(self.err(format!(
7301 "expected segment id literal after AS OF SEGMENT, got {other:?}"
7302 )));
7303 }
7304 };
7305 Some(id)
7306 } else {
7307 None
7308 };
7309 let alias = self.parse_optional_alias();
7310 Ok(TableRef {
7311 name,
7312 alias,
7313 as_of_segment,
7314 unnest_expr: None,
7315 unnest_column_aliases: Vec::new(),
7316 generate_series_args: None,
7317 lateral_subquery: None,
7318 })
7319 }
7320
7321 fn parse_optional_alias_with_columns(&mut self) -> (Option<String>, Vec<String>) {
7327 let alias = self.parse_optional_alias();
7328 if alias.is_none() {
7329 return (None, Vec::new());
7330 }
7331 let mut cols: Vec<String> = Vec::new();
7332 if matches!(self.peek(), Token::LParen) {
7333 self.advance();
7334 while let Token::Ident(s) | Token::QuotedIdent(s) = self.peek().clone() {
7335 self.advance();
7336 cols.push(s);
7337 if matches!(self.peek(), Token::Comma) {
7338 self.advance();
7339 continue;
7340 }
7341 break;
7342 }
7343 if matches!(self.peek(), Token::RParen) {
7344 self.advance();
7345 }
7346 }
7347 (alias, cols)
7348 }
7349
7350 fn parse_from_clause(&mut self) -> Result<FromClause, ParseError> {
7355 let primary = self.parse_table_ref()?;
7356 let mut joins = Vec::new();
7357 loop {
7358 if matches!(self.peek(), Token::Comma) {
7360 self.advance();
7361 let table = self.parse_table_ref()?;
7362 joins.push(FromJoin {
7363 kind: JoinKind::Cross,
7364 table,
7365 on: None,
7366 });
7367 continue;
7368 }
7369 let kind =
7372 match self.peek() {
7373 Token::Inner => {
7374 self.advance();
7375 if !matches!(self.peek(), Token::Join) {
7376 return Err(self
7377 .err(format!("expected JOIN after INNER, got {:?}", self.peek())));
7378 }
7379 self.advance();
7380 JoinKind::Inner
7381 }
7382 Token::Left => {
7383 self.advance();
7384 if matches!(self.peek(), Token::Outer) {
7385 self.advance();
7386 }
7387 if !matches!(self.peek(), Token::Join) {
7388 return Err(self.err(format!(
7389 "expected JOIN after LEFT [OUTER], got {:?}",
7390 self.peek()
7391 )));
7392 }
7393 self.advance();
7394 JoinKind::Left
7395 }
7396 Token::Cross => {
7397 self.advance();
7398 if !matches!(self.peek(), Token::Join) {
7399 return Err(self
7400 .err(format!("expected JOIN after CROSS, got {:?}", self.peek())));
7401 }
7402 self.advance();
7403 JoinKind::Cross
7404 }
7405 Token::Join => {
7406 self.advance();
7407 JoinKind::Inner
7408 }
7409 _ => break,
7410 };
7411 let table = self.parse_table_ref()?;
7412 let on = if matches!(self.peek(), Token::On) {
7413 self.advance();
7414 Some(self.parse_expr(0)?)
7415 } else if kind == JoinKind::Cross {
7416 None
7417 } else {
7418 return Err(self.err(format!(
7419 "expected ON after {:?} JOIN, got {:?}",
7420 kind,
7421 self.peek()
7422 )));
7423 };
7424 joins.push(FromJoin { kind, table, on });
7425 }
7426 Ok(FromClause { primary, joins })
7427 }
7428
7429 fn parse_optional_alias(&mut self) -> Option<String> {
7434 if matches!(self.peek(), Token::As) {
7435 self.advance();
7436 if let Token::Ident(_) | Token::QuotedIdent(_) = self.peek() {
7441 return self.expect_ident_like().ok();
7442 }
7443 return None;
7444 }
7445 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
7453 if is_alias_stopword(s) {
7454 return None;
7455 }
7456 return self.expect_ident_like().ok();
7457 }
7458 None
7459 }
7460
7461 fn parse_expr(&mut self, min_prec: u8) -> Result<Expr, ParseError> {
7463 let mut lhs = self.parse_unary()?;
7464 while let Some((op, prec)) = binop_from(self.peek()) {
7465 if prec < min_prec {
7466 break;
7467 }
7468 self.advance();
7469 let any_kind = match self.peek() {
7474 Token::All if matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) => {
7475 Some(false)
7476 }
7477 Token::Ident(s) | Token::QuotedIdent(s)
7478 if (s.eq_ignore_ascii_case("any") || s.eq_ignore_ascii_case("all"))
7479 && matches!(self.tokens.get(self.pos + 1), Some(Token::LParen)) =>
7480 {
7481 Some(s.eq_ignore_ascii_case("any"))
7482 }
7483 _ => None,
7484 };
7485 if let Some(is_any) = any_kind {
7486 self.advance(); self.advance(); let arr = self.parse_expr(0)?;
7489 if !matches!(self.peek(), Token::RParen) {
7490 return Err(self.err(alloc::format!(
7491 "expected ')' after ANY/ALL argument, got {:?}",
7492 self.peek()
7493 )));
7494 }
7495 self.advance();
7496 lhs = Expr::AnyAll {
7497 expr: Box::new(lhs),
7498 op,
7499 array: Box::new(arr),
7500 is_any,
7501 };
7502 continue;
7503 }
7504 let rhs = self.parse_expr(prec + 1)?;
7505 lhs = Expr::Binary {
7506 lhs: Box::new(lhs),
7507 op,
7508 rhs: Box::new(rhs),
7509 };
7510 }
7511 Ok(lhs)
7512 }
7513
7514 fn parse_unary(&mut self) -> Result<Expr, ParseError> {
7515 match self.peek() {
7516 Token::Not => {
7517 self.advance();
7518 let e = self.parse_expr(3)?;
7521 Ok(Expr::Unary {
7522 op: UnOp::Not,
7523 expr: Box::new(e),
7524 })
7525 }
7526 Token::Minus => {
7527 self.advance();
7528 let e = self.parse_expr(8)?;
7531 Ok(Expr::Unary {
7532 op: UnOp::Neg,
7533 expr: Box::new(e),
7534 })
7535 }
7536 Token::Tilde => {
7537 self.advance();
7538 let e = self.parse_expr(8)?;
7540 Ok(Expr::Unary {
7541 op: UnOp::BitNot,
7542 expr: Box::new(e),
7543 })
7544 }
7545 _ => self.parse_atom(),
7546 }
7547 }
7548
7549 fn parse_atom(&mut self) -> Result<Expr, ParseError> {
7550 let tok_pos = self.pos;
7551 match self.advance() {
7552 Token::Integer(n) => Ok(Expr::Literal(Literal::Integer(n))),
7553 Token::Float(x) => Ok(Expr::Literal(Literal::Float(x))),
7554 Token::String(s) => Ok(Expr::Literal(Literal::String(s))),
7555 Token::True => Ok(Expr::Literal(Literal::Bool(true))),
7556 Token::False => Ok(Expr::Literal(Literal::Bool(false))),
7557 Token::Null => Ok(Expr::Literal(Literal::Null)),
7558 Token::Placeholder(n) => Ok(Expr::Placeholder(n)),
7562 Token::LParen => {
7563 if matches!(self.peek(), Token::Select) {
7567 let inner = self.parse_select_stmt()?;
7568 match self.advance() {
7569 Token::RParen => {
7570 let Statement::Select(s) = inner else {
7571 unreachable!("parse_select_stmt returns Select")
7572 };
7573 Ok(Expr::ScalarSubquery(Box::new(s)))
7574 }
7575 other => Err(ParseError {
7576 message: format!("expected ')' after scalar subquery, got {other:?}"),
7577 token_pos: self.pos.saturating_sub(1),
7578 }),
7579 }
7580 } else {
7581 let e = self.parse_expr(0)?;
7582 match self.advance() {
7583 Token::RParen => Ok(e),
7584 other => Err(ParseError {
7585 message: format!("expected ')', got {other:?}"),
7586 token_pos: self.pos.saturating_sub(1),
7587 }),
7588 }
7589 }
7590 }
7591 Token::LBracket => self.parse_vector_literal_body(),
7592 Token::Extract => self.parse_extract_atom(),
7593 Token::Interval => self.parse_interval_atom(),
7594 Token::Left if matches!(self.peek(), Token::LParen) => {
7601 self.advance(); let mut args = Vec::new();
7603 if !matches!(self.peek(), Token::RParen) {
7604 loop {
7605 args.push(self.parse_expr(0)?);
7606 match self.peek() {
7607 Token::Comma => {
7608 self.advance();
7609 }
7610 Token::RParen => break,
7611 other => {
7612 return Err(self.err(alloc::format!(
7613 "expected ',' or ')' in left() args, got {other:?}"
7614 )));
7615 }
7616 }
7617 }
7618 }
7619 self.advance(); Ok(Expr::FunctionCall {
7621 name: "left".into(),
7622 args,
7623 })
7624 }
7625 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("exists") => {
7630 self.parse_exists_atom(false)
7631 }
7632 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("case") => {
7636 self.parse_case_atom()
7637 }
7638 Token::Ident(s) | Token::QuotedIdent(s)
7642 if s.eq_ignore_ascii_case("array") && matches!(self.peek(), Token::LBracket) =>
7643 {
7644 self.advance(); let mut items: Vec<Expr> = Vec::new();
7646 if !matches!(self.peek(), Token::RBracket) {
7647 loop {
7648 items.push(self.parse_expr(0)?);
7649 match self.peek() {
7650 Token::Comma => {
7651 self.advance();
7652 }
7653 Token::RBracket => break,
7654 other => {
7655 return Err(self.err(alloc::format!(
7656 "expected ',' or ']' in ARRAY literal, got {other:?}"
7657 )));
7658 }
7659 }
7660 }
7661 }
7662 self.advance(); Ok(Expr::Array(items))
7664 }
7665 Token::Ident(s) | Token::QuotedIdent(s)
7680 if s.eq_ignore_ascii_case("match") && matches!(self.peek(), Token::LParen) =>
7681 {
7682 self.parse_match_against_atom()
7683 }
7684 Token::Ident(s) | Token::QuotedIdent(s) => self.finish_ident_atom(s),
7685 other => Err(ParseError {
7686 message: format!("unexpected token {other:?} in expression"),
7687 token_pos: tok_pos,
7688 }),
7689 }
7690 .and_then(|atom| self.finish_postfix_casts(atom))
7692 }
7693
7694 fn finish_postfix_casts(&mut self, mut expr: Expr) -> Result<Expr, ParseError> {
7697 loop {
7698 if matches!(self.peek(), Token::DoubleColon) {
7699 self.advance();
7700 let target = match self.advance() {
7705 Token::Ident(s) => match s.to_ascii_lowercase().as_str() {
7706 "int" | "integer" | "int4" => {
7707 if matches!(self.peek(), Token::LBracket)
7708 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7709 {
7710 self.advance();
7711 self.advance();
7712 CastTarget::IntArray
7713 } else {
7714 CastTarget::Int
7715 }
7716 }
7717 "bigint" | "int8" => {
7718 if matches!(self.peek(), Token::LBracket)
7719 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7720 {
7721 self.advance();
7722 self.advance();
7723 CastTarget::BigIntArray
7724 } else {
7725 CastTarget::BigInt
7726 }
7727 }
7728 "float" | "double" | "real" => CastTarget::Float,
7729 "text" => {
7730 if matches!(self.peek(), Token::LBracket)
7732 && matches!(self.tokens.get(self.pos + 1), Some(Token::RBracket))
7733 {
7734 self.advance();
7735 self.advance();
7736 CastTarget::TextArray
7737 } else {
7738 CastTarget::Text
7739 }
7740 }
7741 "bool" | "boolean" => CastTarget::Bool,
7742 "vector" => CastTarget::Vector,
7743 "date" => CastTarget::Date,
7744 "timestamp" | "datetime" => CastTarget::Timestamp,
7745 "timestamptz" => CastTarget::Timestamptz,
7746 "interval" => CastTarget::Interval,
7747 "json" => CastTarget::Json,
7748 "jsonb" => CastTarget::Jsonb,
7749 "regtype" => CastTarget::RegType,
7750 "regclass" => CastTarget::RegClass,
7751 "tsvector" => CastTarget::TsVector,
7755 "tsquery" => CastTarget::TsQuery,
7756 "uuid" => CastTarget::Uuid,
7759 "bytea" => CastTarget::Bytea,
7764 "inet" | "cidr" | "macaddr" => CastTarget::Text,
7769 other => {
7770 return Err(ParseError {
7771 message: format!("unsupported cast target `::{other}`"),
7772 token_pos: self.pos.saturating_sub(1),
7773 });
7774 }
7775 },
7776 Token::Interval => CastTarget::Interval,
7777 other => {
7778 return Err(ParseError {
7779 message: format!("expected type ident after `::`, got {other:?}"),
7780 token_pos: self.pos.saturating_sub(1),
7781 });
7782 }
7783 };
7784 expr = Expr::Cast {
7785 expr: Box::new(expr),
7786 target,
7787 };
7788 continue;
7789 }
7790 if matches!(self.peek(), Token::Is) {
7791 self.advance();
7792 let negated = if matches!(self.peek(), Token::Not) {
7793 self.advance();
7794 true
7795 } else {
7796 false
7797 };
7798 if matches!(self.peek(), Token::Distinct) {
7801 self.advance();
7802 if !matches!(self.peek(), Token::From) {
7803 return Err(self.err(format!(
7804 "expected FROM after IS{} DISTINCT, got {:?}",
7805 if negated { " NOT" } else { "" },
7806 self.peek()
7807 )));
7808 }
7809 self.advance();
7810 let rhs = self.parse_expr(20)?;
7814 let op = if negated {
7815 BinOp::IsNotDistinctFrom
7816 } else {
7817 BinOp::IsDistinctFrom
7818 };
7819 expr = Expr::Binary {
7820 op,
7821 lhs: Box::new(expr),
7822 rhs: Box::new(rhs),
7823 };
7824 continue;
7825 }
7826 if !matches!(self.peek(), Token::Null) {
7827 return Err(self.err(format!(
7828 "expected NULL or DISTINCT after IS{}, got {:?}",
7829 if negated { " NOT" } else { "" },
7830 self.peek()
7831 )));
7832 }
7833 self.advance();
7834 expr = Expr::IsNull {
7835 expr: Box::new(expr),
7836 negated,
7837 };
7838 continue;
7839 }
7840 let negated = if matches!(self.peek(), Token::Not) {
7844 let next = self.tokens.get(self.pos + 1);
7845 matches!(next, Some(Token::Between | Token::In | Token::Like))
7846 } else {
7847 false
7848 };
7849 if negated {
7850 self.advance();
7851 }
7852 if matches!(self.peek(), Token::Between) {
7853 expr = self.parse_between_tail(expr, negated)?;
7854 continue;
7855 }
7856 if matches!(self.peek(), Token::In) {
7857 expr = self.parse_in_tail(expr, negated)?;
7858 continue;
7859 }
7860 if matches!(self.peek(), Token::Like) {
7861 self.advance();
7862 let pattern = self.parse_expr(5)?;
7865 expr = Expr::Like {
7866 expr: Box::new(expr),
7867 pattern: Box::new(pattern),
7868 negated,
7869 };
7870 continue;
7871 }
7872 if matches!(self.peek(), Token::LBracket) {
7876 self.advance();
7877 let index = self.parse_expr(0)?;
7878 if !matches!(self.peek(), Token::RBracket) {
7879 return Err(self.err(alloc::format!(
7880 "expected ']' after array index, got {:?}",
7881 self.peek()
7882 )));
7883 }
7884 self.advance();
7885 expr = Expr::ArraySubscript {
7886 target: Box::new(expr),
7887 index: Box::new(index),
7888 };
7889 continue;
7890 }
7891 return Ok(expr);
7892 }
7893 }
7894
7895 fn parse_between_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
7899 self.advance(); let low = self.parse_expr(5)?;
7901 if !matches!(self.peek(), Token::And) {
7902 return Err(self.err(format!(
7903 "expected AND after BETWEEN low bound, got {:?}",
7904 self.peek()
7905 )));
7906 }
7907 self.advance();
7908 let high = self.parse_expr(5)?;
7909 let target = Box::new(expr);
7910 let combined = Expr::Binary {
7911 lhs: Box::new(Expr::Binary {
7912 lhs: target.clone(),
7913 op: BinOp::GtEq,
7914 rhs: Box::new(low),
7915 }),
7916 op: BinOp::And,
7917 rhs: Box::new(Expr::Binary {
7918 lhs: target,
7919 op: BinOp::LtEq,
7920 rhs: Box::new(high),
7921 }),
7922 };
7923 Ok(maybe_not(combined, negated))
7924 }
7925
7926 fn parse_with_cte_then_select(&mut self) -> Result<Statement, ParseError> {
7931 let mut recursive = false;
7936 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
7937 && s.eq_ignore_ascii_case("recursive")
7938 {
7939 self.advance();
7940 recursive = true;
7941 }
7942 let mut ctes = Vec::new();
7943 loop {
7944 let name = self.expect_ident_like()?;
7945 let column_overrides: Vec<String> = if matches!(self.peek(), Token::LParen) {
7949 self.advance();
7950 let mut names = Vec::new();
7951 loop {
7952 names.push(self.expect_ident_like()?);
7953 if matches!(self.peek(), Token::Comma) {
7954 self.advance();
7955 continue;
7956 }
7957 break;
7958 }
7959 if !matches!(self.peek(), Token::RParen) {
7960 return Err(self.err(format!(
7961 "expected ')' to close CTE column list, got {:?}",
7962 self.peek()
7963 )));
7964 }
7965 self.advance();
7966 names
7967 } else {
7968 Vec::new()
7969 };
7970 if !matches!(self.peek(), Token::As) {
7974 return Err(self.err(format!(
7975 "expected AS after CTE name {name:?}, got {:?}",
7976 self.peek()
7977 )));
7978 }
7979 self.advance();
7980 if !matches!(self.peek(), Token::LParen) {
7981 return Err(self.err(format!(
7982 "expected '(' after AS in WITH clause, got {:?}",
7983 self.peek()
7984 )));
7985 }
7986 self.advance();
7987 if !matches!(self.peek(), Token::Select) {
7988 return Err(self.err(format!("WITH body must be a SELECT, got {:?}", self.peek())));
7989 }
7990 let inner = self.parse_select_stmt()?;
7991 if !matches!(self.peek(), Token::RParen) {
7992 return Err(self.err(format!(
7993 "expected ')' after CTE body, got {:?}",
7994 self.peek()
7995 )));
7996 }
7997 self.advance();
7998 let Statement::Select(body) = inner else {
7999 unreachable!("parse_select_stmt returns Select")
8000 };
8001 ctes.push(crate::ast::Cte {
8002 name,
8003 body,
8004 recursive,
8005 column_overrides,
8006 });
8007 if matches!(self.peek(), Token::Comma) {
8008 self.advance();
8009 continue;
8010 }
8011 break;
8012 }
8013 if !matches!(self.peek(), Token::Select) {
8015 return Err(self.err(format!(
8016 "expected SELECT after WITH clause, got {:?}",
8017 self.peek()
8018 )));
8019 }
8020 let body_stmt = self.parse_select_stmt()?;
8021 let Statement::Select(mut body) = body_stmt else {
8022 unreachable!()
8023 };
8024 body.ctes = ctes;
8025 Ok(Statement::Select(body))
8026 }
8027
8028 fn parse_case_atom(&mut self) -> Result<Expr, ParseError> {
8037 let operand = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("when")) {
8041 None
8042 } else {
8043 Some(Box::new(self.parse_expr(0)?))
8044 };
8045 let mut branches: Vec<(Expr, Expr)> = Vec::new();
8046 loop {
8047 match self.peek() {
8048 Token::Ident(s) if s.eq_ignore_ascii_case("when") => {
8049 self.advance();
8050 let cond = self.parse_expr(0)?;
8051 match self.peek() {
8052 Token::Ident(t) if t.eq_ignore_ascii_case("then") => {
8053 self.advance();
8054 }
8055 other => {
8056 return Err(self.err(alloc::format!(
8057 "expected THEN after CASE WHEN <expr>, got {other:?}"
8058 )));
8059 }
8060 }
8061 let value = self.parse_expr(0)?;
8062 branches.push((cond, value));
8063 }
8064 _ => break,
8065 }
8066 }
8067 if branches.is_empty() {
8068 return Err(self.err("CASE requires at least one WHEN … THEN … branch".into()));
8069 }
8070 let else_branch = if matches!(self.peek(), Token::Ident(s) if s.eq_ignore_ascii_case("else"))
8071 {
8072 self.advance();
8073 Some(Box::new(self.parse_expr(0)?))
8074 } else {
8075 None
8076 };
8077 match self.peek() {
8078 Token::Ident(s) if s.eq_ignore_ascii_case("end") => {
8079 self.advance();
8080 }
8081 other => {
8082 return Err(self.err(alloc::format!(
8083 "expected END to close CASE expression, got {other:?}"
8084 )));
8085 }
8086 }
8087 Ok(Expr::Case {
8088 operand,
8089 branches,
8090 else_branch,
8091 })
8092 }
8093
8094 fn parse_exists_atom(&mut self, negated: bool) -> Result<Expr, ParseError> {
8095 if !matches!(self.peek(), Token::LParen) {
8096 return Err(self.err(format!("expected '(' after EXISTS, got {:?}", self.peek())));
8097 }
8098 self.advance();
8099 let inner = self.parse_select_stmt()?;
8100 if !matches!(self.peek(), Token::RParen) {
8101 return Err(self.err(format!(
8102 "expected ')' after EXISTS-subquery, got {:?}",
8103 self.peek()
8104 )));
8105 }
8106 self.advance();
8107 let Statement::Select(s) = inner else {
8108 unreachable!("parse_select_stmt returns Select")
8109 };
8110 Ok(Expr::Exists {
8111 subquery: Box::new(s),
8112 negated,
8113 })
8114 }
8115
8116 fn parse_in_tail(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParseError> {
8117 self.advance(); if !matches!(self.peek(), Token::LParen) {
8119 return Err(self.err(format!("expected '(' after IN, got {:?}", self.peek())));
8120 }
8121 self.advance();
8122 if matches!(self.peek(), Token::Select) {
8124 let inner = self.parse_select_stmt()?;
8125 if !matches!(self.peek(), Token::RParen) {
8126 return Err(self.err(format!(
8127 "expected ')' after IN-subquery, got {:?}",
8128 self.peek()
8129 )));
8130 }
8131 self.advance();
8132 let Statement::Select(s) = inner else {
8133 unreachable!("parse_select_stmt always returns Statement::Select")
8134 };
8135 return Ok(Expr::InSubquery {
8136 expr: Box::new(expr),
8137 subquery: Box::new(s),
8138 negated,
8139 });
8140 }
8141 let mut elements = Vec::new();
8142 if !matches!(self.peek(), Token::RParen) {
8143 loop {
8144 elements.push(self.parse_expr(0)?);
8145 match self.peek() {
8146 Token::Comma => {
8147 self.advance();
8148 }
8149 Token::RParen => break,
8150 other => {
8151 return Err(
8152 self.err(format!("expected ',' or ')' in IN list, got {other:?}"))
8153 );
8154 }
8155 }
8156 }
8157 }
8158 self.advance(); let target = Box::new(expr);
8160 let combined = if elements.is_empty() {
8161 Expr::Literal(Literal::Bool(false))
8162 } else {
8163 let mut iter = elements.into_iter();
8164 let first = iter.next().unwrap();
8165 let mut acc = Expr::Binary {
8166 lhs: target.clone(),
8167 op: BinOp::Eq,
8168 rhs: Box::new(first),
8169 };
8170 for elt in iter {
8171 acc = Expr::Binary {
8172 lhs: Box::new(acc),
8173 op: BinOp::Or,
8174 rhs: Box::new(Expr::Binary {
8175 lhs: target.clone(),
8176 op: BinOp::Eq,
8177 rhs: Box::new(elt),
8178 }),
8179 };
8180 }
8181 acc
8182 };
8183 Ok(maybe_not(combined, negated))
8184 }
8185
8186 fn parse_match_against_atom(&mut self) -> Result<Expr, ParseError> {
8207 if !matches!(self.peek(), Token::LParen) {
8210 return Err(self.err(alloc::format!(
8211 "expected '(' after MATCH, got {:?}",
8212 self.peek()
8213 )));
8214 }
8215 self.advance();
8216 let mut cols: Vec<Expr> = Vec::new();
8217 loop {
8218 cols.push(self.parse_expr(0)?);
8219 match self.peek() {
8220 Token::Comma => {
8221 self.advance();
8222 }
8223 Token::RParen => break,
8224 other => {
8225 return Err(self.err(alloc::format!(
8226 "expected ',' or ')' in MATCH column list, got {other:?}"
8227 )));
8228 }
8229 }
8230 }
8231 self.advance(); match self.peek() {
8234 Token::Ident(s) | Token::QuotedIdent(s) if s.eq_ignore_ascii_case("against") => {
8235 self.advance();
8236 }
8237 other => {
8238 return Err(self.err(alloc::format!(
8239 "expected AGAINST after MATCH column list, got {other:?}"
8240 )));
8241 }
8242 }
8243 if !matches!(self.peek(), Token::LParen) {
8244 return Err(self.err(alloc::format!(
8245 "expected '(' after AGAINST, got {:?}",
8246 self.peek()
8247 )));
8248 }
8249 self.advance();
8250 let term = match self.advance() {
8260 Token::String(s) => Expr::Literal(crate::ast::Literal::String(s)),
8261 Token::Placeholder(n) => Expr::Placeholder(n),
8262 Token::Ident(s) | Token::QuotedIdent(s) => Expr::Column(crate::ast::ColumnName {
8263 qualifier: None,
8264 name: s,
8265 }),
8266 other => {
8267 return Err(self.err(alloc::format!(
8268 "MATCH ... AGAINST(<term>) expects a string literal, \
8269 bound parameter, or column ref, got {other:?}"
8270 )));
8271 }
8272 };
8273 loop {
8278 match self.peek() {
8279 Token::In => {
8282 self.advance();
8283 }
8284 Token::Ident(s) | Token::QuotedIdent(s)
8285 if s.eq_ignore_ascii_case("natural")
8286 || s.eq_ignore_ascii_case("language")
8287 || s.eq_ignore_ascii_case("boolean")
8288 || s.eq_ignore_ascii_case("mode")
8289 || s.eq_ignore_ascii_case("with")
8290 || s.eq_ignore_ascii_case("query")
8291 || s.eq_ignore_ascii_case("expansion") =>
8292 {
8293 self.advance();
8294 }
8295 _ => break,
8296 }
8297 }
8298 if !matches!(self.peek(), Token::RParen) {
8299 return Err(self.err(alloc::format!(
8300 "expected ')' to close AGAINST, got {:?}",
8301 self.peek()
8302 )));
8303 }
8304 self.advance();
8305 let simple_lit = || Expr::Literal(crate::ast::Literal::String(String::from("simple")));
8308 let plainto = Expr::FunctionCall {
8309 name: String::from("plainto_tsquery"),
8310 args: alloc::vec![simple_lit(), term.clone()],
8311 };
8312 let mut folded: Option<Expr> = None;
8313 for col in cols {
8314 let to_tsv = Expr::FunctionCall {
8315 name: String::from("to_tsvector"),
8316 args: alloc::vec![simple_lit(), col],
8317 };
8318 let leaf = Expr::Binary {
8319 lhs: Box::new(to_tsv),
8320 op: crate::ast::BinOp::TsMatch,
8321 rhs: Box::new(plainto.clone()),
8322 };
8323 folded = Some(match folded {
8324 None => leaf,
8325 Some(prev) => Expr::Binary {
8326 lhs: Box::new(prev),
8327 op: crate::ast::BinOp::Or,
8328 rhs: Box::new(leaf),
8329 },
8330 });
8331 }
8332 match folded {
8333 Some(e) => Ok(e),
8334 None => Err(self.err(String::from(
8335 "MATCH(...) AGAINST(...) requires at least one column",
8336 ))),
8337 }
8338 }
8339
8340 fn parse_extract_atom(&mut self) -> Result<Expr, ParseError> {
8341 if !matches!(self.peek(), Token::LParen) {
8342 return Err(self.err(format!("expected '(' after EXTRACT, got {:?}", self.peek())));
8343 }
8344 self.advance();
8345 let field_name = self.expect_ident_like()?;
8346 let field = match field_name.to_ascii_lowercase().as_str() {
8347 "year" => ExtractField::Year,
8348 "month" => ExtractField::Month,
8349 "day" => ExtractField::Day,
8350 "hour" => ExtractField::Hour,
8351 "minute" => ExtractField::Minute,
8352 "second" => ExtractField::Second,
8353 "microsecond" | "microseconds" => ExtractField::Microsecond,
8354 "epoch" => ExtractField::Epoch,
8355 other => {
8356 return Err(self.err(format!(
8357 "unknown EXTRACT field {other:?}; \
8358 supported: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSECOND, EPOCH"
8359 )));
8360 }
8361 };
8362 if !matches!(self.peek(), Token::From) {
8363 return Err(self.err(format!(
8364 "expected FROM after EXTRACT field, got {:?}",
8365 self.peek()
8366 )));
8367 }
8368 self.advance();
8369 let source = self.parse_expr(0)?;
8370 if !matches!(self.peek(), Token::RParen) {
8371 return Err(self.err(format!(
8372 "expected ')' to close EXTRACT, got {:?}",
8373 self.peek()
8374 )));
8375 }
8376 self.advance();
8377 Ok(Expr::Extract {
8378 field,
8379 source: Box::new(source),
8380 })
8381 }
8382
8383 fn parse_interval_atom(&mut self) -> Result<Expr, ParseError> {
8388 let tok = self.advance();
8389 let Token::String(text) = tok else {
8390 return Err(self.err(format!(
8391 "expected string literal after INTERVAL, got {tok:?}"
8392 )));
8393 };
8394 let (months, micros) = parse_interval_text(&text).ok_or_else(|| ParseError {
8395 message: format!(
8396 "cannot parse INTERVAL {text:?}; \
8397 expected `<n> <unit> [<n> <unit> ...]` with units \
8398 microsecond[s], millisecond[s], second[s], minute[s], \
8399 hour[s], day[s], week[s], month[s], year[s]"
8400 ),
8401 token_pos: self.pos.saturating_sub(1),
8402 })?;
8403 Ok(Expr::Literal(Literal::Interval {
8404 months,
8405 micros,
8406 text,
8407 }))
8408 }
8409
8410 fn parse_vector_literal_body(&mut self) -> Result<Expr, ParseError> {
8411 let mut elems = Vec::new();
8412 if matches!(self.peek(), Token::RBracket) {
8413 self.advance();
8414 return Ok(Expr::Literal(Literal::Vector(elems)));
8415 }
8416 loop {
8417 let e = self.parse_expr(0)?;
8418 let x = extract_numeric_literal(&e).ok_or_else(|| ParseError {
8419 message: format!("vector element must be a numeric literal, got {e:?}"),
8420 token_pos: self.pos,
8421 })?;
8422 elems.push(x);
8423 match self.peek() {
8424 Token::Comma => {
8425 self.advance();
8426 }
8427 Token::RBracket => {
8428 self.advance();
8429 break;
8430 }
8431 other => {
8432 return Err(self.err(format!("expected ',' or ']' in vector, got {other:?}")));
8433 }
8434 }
8435 }
8436 Ok(Expr::Literal(Literal::Vector(elems)))
8437 }
8438
8439 fn parse_null_treatment_modifier(&mut self) -> NullTreatment {
8448 let Token::Ident(s) = self.peek().clone() else {
8449 return NullTreatment::Respect;
8450 };
8451 let is_ignore = s.eq_ignore_ascii_case("ignore");
8452 let is_respect = s.eq_ignore_ascii_case("respect");
8453 if !is_ignore && !is_respect {
8454 return NullTreatment::Respect;
8455 }
8456 if self.pos + 1 < self.tokens.len()
8459 && let Token::Ident(s2) = &self.tokens[self.pos + 1]
8460 && s2.eq_ignore_ascii_case("nulls")
8461 {
8462 self.advance();
8463 self.advance();
8464 return if is_ignore {
8465 NullTreatment::Ignore
8466 } else {
8467 NullTreatment::Respect
8468 };
8469 }
8470 NullTreatment::Respect
8471 }
8472
8473 #[allow(clippy::type_complexity)] fn parse_over_clause(
8476 &mut self,
8477 ) -> Result<(Vec<Expr>, Vec<(Expr, bool)>, Option<WindowFrame>), ParseError> {
8478 if !matches!(self.peek(), Token::LParen) {
8479 return Err(self.err(format!("expected '(' after OVER, got {:?}", self.peek())));
8480 }
8481 self.advance();
8482 let mut partition_by = Vec::new();
8483 let mut order_by = Vec::new();
8484 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8486 && s.eq_ignore_ascii_case("partition")
8487 {
8488 self.advance();
8489 if !matches!(self.peek(), Token::By) {
8490 return Err(self.err(format!(
8491 "expected BY after PARTITION, got {:?}",
8492 self.peek()
8493 )));
8494 }
8495 self.advance();
8496 loop {
8497 partition_by.push(self.parse_expr(0)?);
8498 if matches!(self.peek(), Token::Comma) {
8499 self.advance();
8500 continue;
8501 }
8502 break;
8503 }
8504 }
8505 if matches!(self.peek(), Token::Order) {
8507 self.advance();
8508 if !matches!(self.peek(), Token::By) {
8509 return Err(self.err(format!("expected BY after ORDER, got {:?}", self.peek())));
8510 }
8511 self.advance();
8512 loop {
8513 let e = self.parse_expr(0)?;
8514 let desc = if matches!(self.peek(), Token::Desc) {
8515 self.advance();
8516 true
8517 } else if matches!(self.peek(), Token::Asc) {
8518 self.advance();
8519 false
8520 } else {
8521 false
8522 };
8523 order_by.push((e, desc));
8524 if matches!(self.peek(), Token::Comma) {
8525 self.advance();
8526 continue;
8527 }
8528 break;
8529 }
8530 }
8531 let mut frame: Option<WindowFrame> = None;
8535 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek() {
8536 let kind = if s.eq_ignore_ascii_case("rows") {
8537 Some(FrameKind::Rows)
8538 } else if s.eq_ignore_ascii_case("range") {
8539 Some(FrameKind::Range)
8540 } else {
8541 None
8542 };
8543 if let Some(kind) = kind {
8544 self.advance();
8545 frame = Some(self.parse_frame_tail(kind)?);
8546 }
8547 }
8548 if !matches!(self.peek(), Token::RParen) {
8549 return Err(self.err(format!(
8550 "expected ')' to close OVER clause, got {:?}",
8551 self.peek()
8552 )));
8553 }
8554 self.advance();
8555 Ok((partition_by, order_by, frame))
8556 }
8557
8558 fn parse_frame_tail(&mut self, kind: FrameKind) -> Result<WindowFrame, ParseError> {
8564 if matches!(self.peek(), Token::Between) {
8565 self.advance();
8566 let start = self.parse_frame_bound()?;
8567 if !matches!(self.peek(), Token::And) {
8568 return Err(self.err(format!("expected AND in frame spec, got {:?}", self.peek())));
8569 }
8570 self.advance();
8571 let end = self.parse_frame_bound()?;
8572 Ok(WindowFrame {
8573 kind,
8574 start,
8575 end: Some(end),
8576 })
8577 } else {
8578 let start = self.parse_frame_bound()?;
8579 Ok(WindowFrame {
8580 kind,
8581 start,
8582 end: None,
8583 })
8584 }
8585 }
8586
8587 fn parse_frame_bound(&mut self) -> Result<FrameBound, ParseError> {
8590 if let Token::Integer(n) = *self.peek() {
8592 self.advance();
8593 let n: u64 = u64::try_from(n).map_err(|_| {
8594 self.err(format!(
8595 "invalid frame offset {n} — expected non-negative integer"
8596 ))
8597 })?;
8598 let dir = self.expect_ident_like()?;
8599 return if dir.eq_ignore_ascii_case("preceding") {
8600 Ok(FrameBound::OffsetPreceding(n))
8601 } else if dir.eq_ignore_ascii_case("following") {
8602 Ok(FrameBound::OffsetFollowing(n))
8603 } else {
8604 Err(self.err(format!(
8605 "expected PRECEDING or FOLLOWING after offset, got {dir:?}"
8606 )))
8607 };
8608 }
8609 let first = self.expect_ident_like()?;
8610 if first.eq_ignore_ascii_case("unbounded") {
8611 let dir = self.expect_ident_like()?;
8612 return if dir.eq_ignore_ascii_case("preceding") {
8613 Ok(FrameBound::UnboundedPreceding)
8614 } else if dir.eq_ignore_ascii_case("following") {
8615 Ok(FrameBound::UnboundedFollowing)
8616 } else {
8617 Err(self.err(format!(
8618 "expected PRECEDING or FOLLOWING after UNBOUNDED, got {dir:?}"
8619 )))
8620 };
8621 }
8622 if first.eq_ignore_ascii_case("current") {
8623 let row = self.expect_ident_like()?;
8624 if !row.eq_ignore_ascii_case("row") {
8625 return Err(self.err(format!("expected ROW after CURRENT, got {row:?}")));
8626 }
8627 return Ok(FrameBound::CurrentRow);
8628 }
8629 Err(self.err(format!(
8630 "expected frame bound (UNBOUNDED/CURRENT/<n>), got {first:?}"
8631 )))
8632 }
8633
8634 fn finish_ident_atom(&mut self, first: String) -> Result<Expr, ParseError> {
8635 if matches!(self.peek(), Token::Dot) {
8636 self.advance();
8637 let name = self.expect_ident_like()?;
8638 if matches!(self.peek(), Token::LParen) {
8644 return self.finish_ident_atom(name);
8645 }
8646 return Ok(Expr::Column(ColumnName {
8647 qualifier: Some(first),
8648 name,
8649 }));
8650 }
8651 if matches!(self.peek(), Token::LParen) {
8652 self.advance();
8653 if first.eq_ignore_ascii_case("count") && matches!(self.peek(), Token::Star) {
8657 self.advance();
8658 if !matches!(self.peek(), Token::RParen) {
8659 return Err(self.err(format!(
8660 "expected ')' after COUNT(*), got {:?}",
8661 self.peek()
8662 )));
8663 }
8664 self.advance();
8665 let null_treatment = self.parse_null_treatment_modifier();
8667 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8668 && s.eq_ignore_ascii_case("over")
8669 {
8670 self.advance();
8671 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8672 return Ok(Expr::WindowFunction {
8673 name: "count_star".into(),
8674 args: Vec::new(),
8675 partition_by,
8676 order_by,
8677 frame,
8678 null_treatment,
8679 });
8680 }
8681 return Ok(Expr::FunctionCall {
8682 name: "count_star".into(),
8683 args: Vec::new(),
8684 });
8685 }
8686 let mut args = Vec::new();
8688 if !matches!(self.peek(), Token::RParen) {
8689 loop {
8690 args.push(self.parse_expr(0)?);
8691 match self.peek() {
8692 Token::Comma => {
8693 self.advance();
8694 }
8695 Token::RParen => break,
8696 other => {
8697 return Err(self.err(format!(
8698 "expected ',' or ')' in function args, got {other:?}"
8699 )));
8700 }
8701 }
8702 }
8703 }
8704 self.advance(); let null_treatment = self.parse_null_treatment_modifier();
8712 if let Token::Ident(s) | Token::QuotedIdent(s) = self.peek()
8713 && s.eq_ignore_ascii_case("over")
8714 {
8715 self.advance();
8716 let (partition_by, order_by, frame) = self.parse_over_clause()?;
8717 return Ok(Expr::WindowFunction {
8718 name: first,
8719 args,
8720 partition_by,
8721 order_by,
8722 frame,
8723 null_treatment,
8724 });
8725 }
8726 return Ok(Expr::FunctionCall { name: first, args });
8727 }
8728 let lc = first.to_ascii_lowercase();
8734 if matches!(
8735 lc.as_str(),
8736 "current_date" | "current_time" | "current_timestamp" | "localtimestamp" | "localtime"
8737 ) {
8738 return Ok(Expr::FunctionCall {
8739 name: lc,
8740 args: Vec::new(),
8741 });
8742 }
8743 Ok(Expr::Column(ColumnName {
8744 qualifier: None,
8745 name: first,
8746 }))
8747 }
8748}
8749
8750fn extract_first_column(expr: &Expr) -> Option<String> {
8758 match expr {
8759 Expr::Column(cn) => Some(cn.name.clone()),
8760 Expr::FunctionCall { args, .. } => args.iter().find_map(extract_first_column),
8761 Expr::Binary { lhs, rhs, .. } => {
8762 extract_first_column(lhs).or_else(|| extract_first_column(rhs))
8763 }
8764 Expr::Unary { expr: e, .. } => extract_first_column(e),
8765 _ => None,
8766 }
8767}
8768
8769fn maybe_not(expr: Expr, negated: bool) -> Expr {
8770 if negated {
8771 Expr::Unary {
8772 op: UnOp::Not,
8773 expr: Box::new(expr),
8774 }
8775 } else {
8776 expr
8777 }
8778}
8779
8780fn binop_from(tok: &Token) -> Option<(BinOp, u8)> {
8781 let pair = match tok {
8782 Token::Or => (BinOp::Or, 1),
8783 Token::And => (BinOp::And, 2),
8784 Token::Eq => (BinOp::Eq, 4),
8785 Token::NotEq => (BinOp::NotEq, 4),
8786 Token::Lt => (BinOp::Lt, 4),
8787 Token::LtEq => (BinOp::LtEq, 4),
8788 Token::Gt => (BinOp::Gt, 4),
8789 Token::GtEq => (BinOp::GtEq, 4),
8790 Token::L2Distance => (BinOp::L2Distance, 5),
8793 Token::InnerProduct => (BinOp::InnerProduct, 5),
8794 Token::CosineDistance => (BinOp::CosineDistance, 5),
8795 Token::Plus => (BinOp::Add, 6),
8796 Token::Minus => (BinOp::Sub, 6),
8797 Token::Concat => (BinOp::Concat, 6),
8800 Token::Pipe => (BinOp::BitOr, 6),
8813 Token::Amp => (BinOp::BitAnd, 6),
8814 Token::Star => (BinOp::Mul, 7),
8815 Token::Slash => (BinOp::Div, 7),
8816 Token::JsonGet => (BinOp::JsonGet, 7),
8820 Token::JsonGetText => (BinOp::JsonGetText, 7),
8821 Token::JsonGetPath => (BinOp::JsonGetPath, 7),
8822 Token::JsonGetPathText => (BinOp::JsonGetPathText, 7),
8823 Token::JsonContains => (BinOp::JsonContains, 7),
8824 Token::TsMatch => (BinOp::TsMatch, 4),
8828 Token::InetContainedBy => (BinOp::InetContainedBy, 4),
8832 Token::InetContainedByEq => (BinOp::InetContainedByEq, 4),
8833 Token::InetContains => (BinOp::InetContains, 4),
8834 Token::InetContainsEq => (BinOp::InetContainsEq, 4),
8835 Token::InetOverlap => (BinOp::InetOverlap, 4),
8836 _ => return None,
8837 };
8838 Some(pair)
8839}
8840
8841#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
8842fn is_alias_stopword(s: &str) -> bool {
8854 matches!(
8855 s.to_ascii_lowercase().as_str(),
8856 "with"
8857 | "on"
8858 | "where"
8859 | "having"
8860 | "group"
8861 | "order"
8862 | "limit"
8863 | "offset"
8864 | "union"
8865 | "except"
8866 | "intersect"
8867 | "returning"
8868 | "set"
8869 | "values"
8870 | "for"
8871 | "lateral"
8872 | "left"
8873 | "right"
8874 | "inner"
8875 | "outer"
8876 | "full"
8877 | "cross"
8878 | "join"
8879 | "natural"
8880 | "using"
8881 | "fetch"
8882 )
8883}
8884
8885fn extract_numeric_literal(e: &Expr) -> Option<f32> {
8886 match e {
8887 Expr::Literal(Literal::Integer(n)) => Some(*n as f32),
8888 Expr::Literal(Literal::Float(x)) => Some(*x as f32),
8889 Expr::Unary {
8890 op: UnOp::Neg,
8891 expr,
8892 } => extract_numeric_literal(expr).map(|x| -x),
8893 _ => None,
8894 }
8895}
8896
8897pub fn parse_interval_text(s: &str) -> Option<(i32, i64)> {
8905 let parts: Vec<&str> = s.split_whitespace().collect();
8906 if parts.is_empty() || !parts.len().is_multiple_of(2) {
8907 return None;
8908 }
8909 let mut months: i32 = 0;
8910 let mut micros: i64 = 0;
8911 let mut i = 0;
8912 while i < parts.len() {
8913 let n: i64 = parts[i].parse().ok()?;
8914 let unit = parts[i + 1].to_ascii_lowercase();
8915 let unit_stripped = unit.strip_suffix('s').unwrap_or(&unit);
8916 match unit_stripped {
8917 "microsecond" => micros = micros.checked_add(n)?,
8918 "millisecond" => micros = micros.checked_add(n.checked_mul(1_000)?)?,
8919 "second" => micros = micros.checked_add(n.checked_mul(1_000_000)?)?,
8920 "minute" => micros = micros.checked_add(n.checked_mul(60_000_000)?)?,
8921 "hour" => micros = micros.checked_add(n.checked_mul(3_600_000_000)?)?,
8922 "day" => micros = micros.checked_add(n.checked_mul(86_400_000_000)?)?,
8923 "week" => micros = micros.checked_add(n.checked_mul(604_800_000_000)?)?,
8924 "month" => {
8925 let n32 = i32::try_from(n).ok()?;
8926 months = months.checked_add(n32)?;
8927 }
8928 "year" => {
8929 let n32 = i32::try_from(n).ok()?;
8930 months = months.checked_add(n32.checked_mul(12)?)?;
8931 }
8932 _ => return None,
8933 }
8934 i += 2;
8935 }
8936 Some((months, micros))
8937}
8938
8939fn map_type_ident_to_column_type_name(ident: &str) -> Option<ColumnTypeName> {
8950 Some(match ident.to_ascii_lowercase().as_str() {
8951 "smallint" | "tinyint" => ColumnTypeName::SmallInt,
8952 "int" | "integer" | "mediumint" => ColumnTypeName::Int,
8953 "bigint" => ColumnTypeName::BigInt,
8954 "float" | "double" | "real" => ColumnTypeName::Float,
8955 "text" => ColumnTypeName::Text,
8956 "bool" | "boolean" => ColumnTypeName::Bool,
8957 "date" => ColumnTypeName::Date,
8958 "timestamp" | "datetime" => ColumnTypeName::Timestamp,
8959 "timestamptz" => ColumnTypeName::Timestamptz,
8960 "json" => ColumnTypeName::Json,
8961 "jsonb" => ColumnTypeName::Jsonb,
8962 "bytea" | "bytes" => ColumnTypeName::Bytes,
8963 "tsvector" => ColumnTypeName::TsVector,
8964 "tsquery" => ColumnTypeName::TsQuery,
8965 "uuid" => ColumnTypeName::Uuid,
8966 "time" => ColumnTypeName::Time,
8967 "year" => ColumnTypeName::Year,
8968 "timetz" => ColumnTypeName::TimeTz,
8969 "money" => ColumnTypeName::Money,
8970 _ => return None,
8971 })
8972}
8973
8974pub fn parse_function_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9001 parse_plpgsql_body(body)
9002}
9003
9004fn parse_plpgsql_body(body: &str) -> Result<PlPgSqlBlock, ParseError> {
9005 let tokens = lexer::tokenize(body).map_err(|e| ParseError {
9009 message: alloc::format!("plpgsql body lex error: {e}"),
9010 token_pos: 0,
9011 })?;
9012 let mut parser = Parser::new(tokens);
9013 parser.parse_plpgsql_block()
9014}
9015
9016#[cfg(test)]
9017mod tests {
9018 use super::*;
9019 use alloc::string::ToString;
9020
9021 fn parse(s: &str) -> Statement {
9022 parse_statement(s).expect("parse ok")
9023 }
9024
9025 fn lit_int(n: i64) -> Expr {
9026 Expr::Literal(Literal::Integer(n))
9027 }
9028
9029 fn col(name: &str) -> Expr {
9030 Expr::Column(ColumnName {
9031 qualifier: None,
9032 name: name.into(),
9033 })
9034 }
9035
9036 #[test]
9037 fn select_single_integer() {
9038 let s = parse("SELECT 1");
9039 let Statement::Select(s) = s else {
9040 panic!("expected SELECT")
9041 };
9042 assert_eq!(s.items.len(), 1);
9043 assert!(s.from.is_none());
9044 assert!(s.where_.is_none());
9045 }
9046
9047 #[test]
9048 fn select_multiple_literal_kinds() {
9049 let s = parse("SELECT 1, 'hi', NULL, TRUE, 1.5");
9050 let Statement::Select(s) = s else {
9051 panic!("expected SELECT")
9052 };
9053 assert_eq!(s.items.len(), 5);
9054 }
9055
9056 #[test]
9057 fn select_wildcard_from_table() {
9058 let s = parse("SELECT * FROM users");
9059 let Statement::Select(s) = s else {
9060 panic!("expected SELECT")
9061 };
9062 assert!(matches!(s.items[..], [SelectItem::Wildcard]));
9063 assert_eq!(s.from.as_ref().unwrap().primary.name, "users");
9064 }
9065
9066 #[test]
9067 fn select_with_table_alias() {
9068 let s = parse("SELECT * FROM users AS u");
9069 let Statement::Select(s) = s else {
9070 panic!("expected SELECT")
9071 };
9072 let t = &s.from.as_ref().unwrap().primary;
9073 assert_eq!(t.name, "users");
9074 assert_eq!(t.alias.as_deref(), Some("u"));
9075 }
9076
9077 #[test]
9078 fn select_with_where_eq() {
9079 let s = parse("SELECT a FROM t WHERE a = 1");
9080 let Statement::Select(s) = s else {
9081 panic!("expected SELECT")
9082 };
9083 let w = s.where_.unwrap();
9084 assert_eq!(
9085 w,
9086 Expr::Binary {
9087 lhs: Box::new(col("a")),
9088 op: BinOp::Eq,
9089 rhs: Box::new(lit_int(1)),
9090 }
9091 );
9092 }
9093
9094 #[test]
9095 fn arithmetic_precedence() {
9096 let s = parse("SELECT 1 + 2 * 3");
9097 let Statement::Select(s) = s else {
9098 panic!("expected SELECT")
9099 };
9100 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9101 panic!("wildcard?")
9102 };
9103 assert_eq!(
9104 expr,
9105 &Expr::Binary {
9106 lhs: Box::new(lit_int(1)),
9107 op: BinOp::Add,
9108 rhs: Box::new(Expr::Binary {
9109 lhs: Box::new(lit_int(2)),
9110 op: BinOp::Mul,
9111 rhs: Box::new(lit_int(3)),
9112 }),
9113 }
9114 );
9115 }
9116
9117 #[test]
9118 fn parentheses_override_precedence() {
9119 let s = parse("SELECT (1 + 2) * 3");
9120 let Statement::Select(s) = s else {
9121 panic!("expected SELECT")
9122 };
9123 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9124 panic!()
9125 };
9126 assert_eq!(
9127 expr,
9128 &Expr::Binary {
9129 lhs: Box::new(Expr::Binary {
9130 lhs: Box::new(lit_int(1)),
9131 op: BinOp::Add,
9132 rhs: Box::new(lit_int(2)),
9133 }),
9134 op: BinOp::Mul,
9135 rhs: Box::new(lit_int(3)),
9136 }
9137 );
9138 }
9139
9140 #[test]
9141 fn not_binds_below_comparison() {
9142 let s = parse("SELECT NOT a = 1 FROM t");
9144 let Statement::Select(s) = s else {
9145 panic!("expected SELECT")
9146 };
9147 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9148 panic!()
9149 };
9150 assert_eq!(
9151 expr,
9152 &Expr::Unary {
9153 op: UnOp::Not,
9154 expr: Box::new(Expr::Binary {
9155 lhs: Box::new(col("a")),
9156 op: BinOp::Eq,
9157 rhs: Box::new(lit_int(1)),
9158 }),
9159 }
9160 );
9161 }
9162
9163 #[test]
9164 fn unary_minus_binds_above_multiplication() {
9165 let s = parse("SELECT -a * 2 FROM t");
9167 let Statement::Select(s) = s else {
9168 panic!("expected SELECT")
9169 };
9170 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9171 panic!()
9172 };
9173 assert_eq!(
9174 expr,
9175 &Expr::Binary {
9176 lhs: Box::new(Expr::Unary {
9177 op: UnOp::Neg,
9178 expr: Box::new(col("a")),
9179 }),
9180 op: BinOp::Mul,
9181 rhs: Box::new(lit_int(2)),
9182 }
9183 );
9184 }
9185
9186 #[test]
9187 fn qualified_column() {
9188 let s = parse("SELECT t.col FROM t");
9189 let Statement::Select(s) = s else {
9190 panic!("expected SELECT")
9191 };
9192 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9193 panic!()
9194 };
9195 assert_eq!(
9196 expr,
9197 &Expr::Column(ColumnName {
9198 qualifier: Some("t".into()),
9199 name: "col".into()
9200 })
9201 );
9202 }
9203
9204 #[test]
9205 fn select_item_alias_with_as() {
9206 let s = parse("SELECT a AS y FROM t");
9207 let Statement::Select(s) = s else {
9208 panic!("expected SELECT")
9209 };
9210 let SelectItem::Expr { alias, .. } = &s.items[0] else {
9211 panic!()
9212 };
9213 assert_eq!(alias.as_deref(), Some("y"));
9214 }
9215
9216 #[test]
9217 fn trailing_semicolon_accepted() {
9218 let s = parse("SELECT 1;");
9219 let Statement::Select(s) = s else {
9220 panic!("expected SELECT")
9221 };
9222 assert_eq!(s.items.len(), 1);
9223 }
9224
9225 #[test]
9226 fn boolean_chain_with_and_or_not() {
9227 let s = parse("SELECT NOT a OR b AND NOT c FROM t");
9229 let Statement::Select(s) = s else {
9230 panic!("expected SELECT")
9231 };
9232 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9233 panic!()
9234 };
9235 let expected = Expr::Binary {
9236 lhs: Box::new(Expr::Unary {
9237 op: UnOp::Not,
9238 expr: Box::new(col("a")),
9239 }),
9240 op: BinOp::Or,
9241 rhs: Box::new(Expr::Binary {
9242 lhs: Box::new(col("b")),
9243 op: BinOp::And,
9244 rhs: Box::new(Expr::Unary {
9245 op: UnOp::Not,
9246 expr: Box::new(col("c")),
9247 }),
9248 }),
9249 };
9250 assert_eq!(expr, &expected);
9251 }
9252
9253 #[test]
9254 fn empty_input_errors() {
9255 assert!(matches!(parse_statement("").unwrap(), Statement::Empty));
9262 assert!(matches!(
9263 parse_statement(" \n\t ").unwrap(),
9264 Statement::Empty
9265 ));
9266 assert!(parse_statement("SELECT FROM WHERE").is_err());
9268 }
9269
9270 #[test]
9271 fn unmatched_paren_errors() {
9272 assert!(parse_statement("SELECT (1 + 2").is_err());
9273 }
9274
9275 #[test]
9276 fn display_round_trip_simple_select() {
9277 let original = parse("SELECT a + 1 FROM t WHERE a > 0");
9278 let text = original.to_string();
9279 let again = parse_statement(&text).expect("re-parse");
9280 assert_eq!(original, again);
9281 }
9282
9283 #[test]
9286 fn create_table_single_column() {
9287 let s = parse("CREATE TABLE foo (a INT)");
9288 let Statement::CreateTable(c) = s else {
9289 panic!("expected CreateTable")
9290 };
9291 assert_eq!(c.name, "foo");
9292 assert_eq!(c.columns.len(), 1);
9293 assert_eq!(c.columns[0].name, "a");
9294 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9295 assert!(c.columns[0].nullable);
9296 }
9297
9298 #[test]
9299 fn create_table_multi_column_with_not_null_mix() {
9300 let s = parse("CREATE TABLE u (id INT NOT NULL, name TEXT, score FLOAT NOT NULL, ok BOOL)");
9301 let Statement::CreateTable(c) = s else {
9302 panic!()
9303 };
9304 assert_eq!(c.columns.len(), 4);
9305 assert_eq!(c.columns[0].ty, ColumnTypeName::Int);
9306 assert!(!c.columns[0].nullable);
9307 assert_eq!(c.columns[1].ty, ColumnTypeName::Text);
9308 assert!(c.columns[1].nullable);
9309 assert_eq!(c.columns[2].ty, ColumnTypeName::Float);
9310 assert!(!c.columns[2].nullable);
9311 assert_eq!(c.columns[3].ty, ColumnTypeName::Bool);
9312 }
9313
9314 #[test]
9315 fn create_table_bigint_supported() {
9316 let s = parse("CREATE TABLE accounts (id BIGINT NOT NULL)");
9317 let Statement::CreateTable(c) = s else {
9318 panic!()
9319 };
9320 assert_eq!(c.columns[0].ty, ColumnTypeName::BigInt);
9321 }
9322
9323 #[test]
9324 fn create_table_vector_default_is_f32() {
9325 let s = parse("CREATE TABLE t (v VECTOR(128))");
9326 let Statement::CreateTable(c) = s else {
9327 panic!()
9328 };
9329 assert_eq!(
9330 c.columns[0].ty,
9331 ColumnTypeName::Vector {
9332 dim: 128,
9333 encoding: VecEncoding::F32,
9334 },
9335 );
9336 }
9337
9338 #[test]
9339 fn create_table_vector_using_sq8() {
9340 for sql in [
9343 "CREATE TABLE t (v VECTOR(128) USING SQ8)",
9344 "CREATE TABLE t (v VECTOR(128) using sq8)",
9345 ] {
9346 let s = parse(sql);
9347 let Statement::CreateTable(c) = s else {
9348 panic!()
9349 };
9350 assert_eq!(
9351 c.columns[0].ty,
9352 ColumnTypeName::Vector {
9353 dim: 128,
9354 encoding: VecEncoding::Sq8,
9355 },
9356 "{sql}",
9357 );
9358 }
9359 }
9360
9361 #[test]
9362 fn create_table_vector_using_unknown_errors() {
9363 let err = parse_statement("CREATE TABLE t (v VECTOR(8) USING PQ8)").unwrap_err();
9372 assert!(
9373 err.message.contains("USING")
9374 || err.message.contains("using")
9375 || err.message.contains("')'")
9376 || err.message.contains("','"),
9377 "expected USING/column-list rejection, got: {}",
9378 err.message
9379 );
9380 }
9381
9382 #[test]
9383 fn vector_using_sq8_display_roundtrips() {
9384 let s = parse("CREATE TABLE t (v VECTOR(64) USING SQ8)");
9387 let Statement::CreateTable(c) = s else {
9388 panic!()
9389 };
9390 assert_eq!(c.columns[0].ty.to_string(), "VECTOR(64) USING SQ8");
9391 }
9392
9393 #[test]
9394 fn parser_recognises_placeholders() {
9395 use crate::ast::{Expr, SelectItem, Statement};
9396 let s = parse("SELECT $1, $2 + 1 FROM t WHERE x = $3");
9398 let Statement::Select(sel) = s else { panic!() };
9399 assert!(matches!(
9400 sel.items[0],
9401 SelectItem::Expr {
9402 expr: Expr::Placeholder(1),
9403 alias: None
9404 }
9405 ));
9406 let SelectItem::Expr {
9408 expr: Expr::Binary { lhs, rhs, .. },
9409 ..
9410 } = &sel.items[1]
9411 else {
9412 panic!()
9413 };
9414 assert!(matches!(**lhs, Expr::Placeholder(2)));
9415 assert!(matches!(**rhs, Expr::Literal(Literal::Integer(1))));
9416 let Some(Expr::Binary { rhs, .. }) = sel.where_.as_ref() else {
9418 panic!()
9419 };
9420 assert!(matches!(**rhs, Expr::Placeholder(3)));
9421 }
9422
9423 #[test]
9424 fn parser_rejects_dollar_zero() {
9425 assert!(parse_statement("SELECT $0").is_err());
9427 }
9428
9429 #[test]
9430 fn placeholder_display_roundtrips() {
9431 let s = parse("SELECT $42 FROM t");
9434 let printed = s.to_string();
9435 assert!(printed.contains("$42"));
9436 let again = parse(&printed);
9437 assert_eq!(s, again);
9438 }
9439
9440 #[test]
9441 fn alter_index_rebuild_bare() {
9442 use crate::ast::{AlterIndexTarget, Statement};
9443 let s = parse("ALTER INDEX my_idx REBUILD");
9444 let Statement::AlterIndex(a) = s else {
9445 panic!("expected AlterIndex, got {s:?}")
9446 };
9447 assert_eq!(a.name, "my_idx");
9448 assert_eq!(a.target, AlterIndexTarget::Rebuild { encoding: None });
9449 }
9450
9451 #[test]
9452 fn alter_index_rebuild_with_encoding() {
9453 use crate::ast::{AlterIndexTarget, Statement};
9454 for (sql, want) in [
9455 (
9456 "ALTER INDEX my_idx REBUILD WITH (encoding = F32)",
9457 VecEncoding::F32,
9458 ),
9459 (
9460 "ALTER INDEX my_idx REBUILD WITH (encoding = sq8)",
9461 VecEncoding::Sq8,
9462 ),
9463 (
9464 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9465 VecEncoding::F16,
9466 ),
9467 ] {
9468 let s = parse(sql);
9469 let Statement::AlterIndex(a) = s else {
9470 panic!("{sql}: expected AlterIndex")
9471 };
9472 assert_eq!(a.name, "my_idx");
9473 assert_eq!(
9474 a.target,
9475 AlterIndexTarget::Rebuild {
9476 encoding: Some(want)
9477 },
9478 "{sql}"
9479 );
9480 }
9481 }
9482
9483 #[test]
9484 fn alter_index_rebuild_unknown_encoding_errors() {
9485 let err = parse_statement("ALTER INDEX my_idx REBUILD WITH (encoding = PQ8)").unwrap_err();
9486 assert!(
9487 err.message.contains("unknown vector encoding"),
9488 "got: {}",
9489 err.message
9490 );
9491 }
9492
9493 #[test]
9494 fn alter_index_rebuild_display_roundtrips() {
9495 for (input, want) in [
9496 ("ALTER INDEX my_idx REBUILD", "ALTER INDEX my_idx REBUILD"),
9497 (
9498 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9499 "ALTER INDEX my_idx REBUILD WITH (encoding = SQ8)",
9500 ),
9501 (
9502 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9503 "ALTER INDEX my_idx REBUILD WITH (encoding = HALF)",
9504 ),
9505 ] {
9506 let s = parse(input);
9507 assert_eq!(s.to_string(), want);
9508 }
9509 }
9510
9511 #[test]
9512 fn create_table_unknown_type_defers_to_engine() {
9513 let stmt = parse_statement("CREATE TABLE x (a xml)").unwrap();
9520 let Statement::CreateTable(t) = stmt else {
9521 panic!("expected CreateTable");
9522 };
9523 assert_eq!(t.columns[0].user_type_ref.as_deref(), Some("xml"));
9524 }
9525
9526 #[test]
9527 fn create_table_missing_table_keyword_errors() {
9528 assert!(parse_statement("CREATE x (a INT)").is_err());
9529 }
9530
9531 #[test]
9532 fn insert_single_value() {
9533 let s = parse("INSERT INTO foo VALUES (42)");
9534 let Statement::Insert(i) = s else {
9535 panic!("expected Insert")
9536 };
9537 assert_eq!(i.table, "foo");
9538 assert_eq!(i.rows.len(), 1);
9539 assert_eq!(i.rows[0].len(), 1);
9540 assert!(matches!(i.rows[0][0], Expr::Literal(Literal::Integer(42))));
9541 }
9542
9543 #[test]
9544 fn insert_multi_value_with_mixed_literals() {
9545 let s = parse("INSERT INTO foo VALUES (1, 'hi', 3.14, TRUE, NULL)");
9546 let Statement::Insert(i) = s else { panic!() };
9547 assert_eq!(i.rows.len(), 1);
9548 assert_eq!(i.rows[0].len(), 5);
9549 }
9550
9551 #[test]
9552 fn insert_missing_into_errors() {
9553 assert!(parse_statement("INSERT foo VALUES (1)").is_err());
9554 }
9555
9556 #[test]
9557 fn create_table_round_trip() {
9558 let original =
9559 parse("CREATE TABLE foo (id BIGINT NOT NULL, label TEXT, score FLOAT NOT NULL)");
9560 let text = original.to_string();
9561 let again = parse_statement(&text).expect("re-parse");
9562 assert_eq!(original, again);
9563 }
9564
9565 #[test]
9566 fn insert_round_trip_with_negation_and_string() {
9567 let original = parse("INSERT INTO t VALUES (-1, 'it''s', NULL)");
9568 let text = original.to_string();
9569 let again = parse_statement(&text).expect("re-parse");
9570 assert_eq!(original, again);
9571 }
9572
9573 #[test]
9574 fn unknown_keyword_at_statement_start_errors() {
9575 let err = parse_statement("FROBNICATE foo SET x = 1").unwrap_err();
9578 assert!(err.message.contains("expected SELECT"));
9579 }
9580
9581 #[test]
9584 fn create_index_basic() {
9585 let s = parse("CREATE INDEX idx_id ON users (id)");
9586 let Statement::CreateIndex(c) = s else {
9587 panic!("expected CreateIndex")
9588 };
9589 assert_eq!(c.name, "idx_id");
9590 assert_eq!(c.table, "users");
9591 assert_eq!(c.column, "id");
9592 }
9593
9594 #[test]
9595 fn create_index_missing_on_errors() {
9596 assert!(parse_statement("CREATE INDEX foo users (id)").is_err());
9597 }
9598
9599 #[test]
9600 fn create_index_missing_paren_errors() {
9601 assert!(parse_statement("CREATE INDEX foo ON users id").is_err());
9602 }
9603
9604 #[test]
9605 fn create_index_round_trip() {
9606 let original = parse("CREATE INDEX by_name ON users (name)");
9607 let again = parse_statement(&original.to_string()).unwrap();
9608 assert_eq!(original, again);
9609 }
9610
9611 #[test]
9614 fn create_unique_index_basic() {
9615 let s = parse("CREATE UNIQUE INDEX uq_x ON t (a)");
9616 let Statement::CreateIndex(c) = s else {
9617 panic!("expected CreateIndex");
9618 };
9619 assert!(c.is_unique);
9620 assert_eq!(c.column, "a");
9621 assert!(c.partial_predicate.is_none());
9622 }
9623
9624 #[test]
9625 fn create_unique_index_partial() {
9626 let s = parse(
9628 "CREATE UNIQUE INDEX idx_email_templates_user_default \
9629 ON email_templates (user_address) WHERE is_default = true",
9630 );
9631 let Statement::CreateIndex(c) = s else {
9632 panic!("expected CreateIndex");
9633 };
9634 assert!(c.is_unique);
9635 assert_eq!(c.table, "email_templates");
9636 assert_eq!(c.column, "user_address");
9637 assert!(c.partial_predicate.is_some());
9638 }
9639
9640 #[test]
9641 fn create_unique_index_composite_with_predicate() {
9642 let s = parse(
9644 "CREATE UNIQUE INDEX uq_calendar_events_instance \
9645 ON calendar_events (calendar_id, uid, recurrence_id) \
9646 WHERE recurrence_id IS NOT NULL",
9647 );
9648 let Statement::CreateIndex(c) = s else {
9649 panic!("expected CreateIndex");
9650 };
9651 assert!(c.is_unique);
9652 assert_eq!(c.column, "calendar_id");
9653 assert_eq!(
9654 c.extra_columns,
9655 vec!["uid".to_string(), "recurrence_id".to_string()]
9656 );
9657 assert!(c.partial_predicate.is_some());
9658 }
9659
9660 #[test]
9661 fn create_unique_index_using_btree_ok() {
9662 let s = parse("CREATE UNIQUE INDEX uq_x ON t USING btree (a)");
9663 assert!(matches!(s, Statement::CreateIndex(ref c) if c.is_unique));
9664 }
9665
9666 #[test]
9667 fn create_unique_index_using_hnsw_rejected() {
9668 let err =
9669 parse_statement("CREATE UNIQUE INDEX uq_v ON t USING hnsw (embedding)").unwrap_err();
9670 assert!(err.message.contains("UNIQUE"), "{}", err.message);
9671 }
9672
9673 #[test]
9674 fn create_unique_index_round_trip() {
9675 let original = parse(
9676 "CREATE UNIQUE INDEX uq_calendar_events_master \
9677 ON calendar_events (calendar_id, uid) WHERE recurrence_id IS NULL",
9678 );
9679 let again = parse_statement(&original.to_string()).unwrap();
9680 assert_eq!(original, again);
9681 }
9682
9683 #[test]
9684 fn create_unique_without_index_errors() {
9685 let err = parse_statement("CREATE UNIQUE TABLE t (a INT)").unwrap_err();
9686 assert!(err.message.contains("INDEX"), "{}", err.message);
9687 }
9688
9689 #[test]
9692 fn create_table_bytea_column() {
9693 let s = parse("CREATE TABLE t (id INT NOT NULL, payload BYTEA NOT NULL)");
9694 let Statement::CreateTable(c) = s else {
9695 panic!("expected CreateTable");
9696 };
9697 assert_eq!(c.columns.len(), 2);
9698 assert_eq!(c.columns[1].ty, ColumnTypeName::Bytes);
9699 assert!(!c.columns[1].nullable);
9700 }
9701
9702 #[test]
9703 fn create_table_bytes_alias_column() {
9704 let s = parse("CREATE TABLE t (blob BYTES)");
9705 let Statement::CreateTable(c) = s else {
9706 panic!("expected CreateTable");
9707 };
9708 assert_eq!(c.columns[0].ty, ColumnTypeName::Bytes);
9709 }
9710
9711 #[test]
9712 fn bytea_round_trip_display() {
9713 let original = parse("CREATE TABLE t (a BYTEA NOT NULL)");
9714 let again = parse_statement(&original.to_string()).unwrap();
9715 assert_eq!(original, again);
9716 }
9717
9718 #[test]
9721 fn begin_commit_rollback_parse_as_unit_variants() {
9722 assert_eq!(parse("BEGIN"), Statement::Begin);
9723 assert_eq!(parse("COMMIT"), Statement::Commit);
9724 assert_eq!(parse("ROLLBACK"), Statement::Rollback);
9725 assert_eq!(parse("BEGIN;"), Statement::Begin);
9727 }
9728
9729 #[test]
9732 fn inner_product_binop_parses() {
9733 let s = parse("SELECT v <#> [1.0, 2.0] FROM t");
9734 let Statement::Select(s) = s else { panic!() };
9735 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9736 panic!()
9737 };
9738 assert!(matches!(
9739 expr,
9740 Expr::Binary {
9741 op: BinOp::InnerProduct,
9742 ..
9743 }
9744 ));
9745 }
9746
9747 #[test]
9748 fn cosine_distance_binop_parses() {
9749 let s = parse("SELECT v <=> [1.0, 2.0] FROM t");
9750 let Statement::Select(s) = s else { panic!() };
9751 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9752 panic!()
9753 };
9754 assert!(matches!(
9755 expr,
9756 Expr::Binary {
9757 op: BinOp::CosineDistance,
9758 ..
9759 }
9760 ));
9761 }
9762
9763 #[test]
9764 fn vector_cast_postfix_wraps_string_literal() {
9765 let s = parse("SELECT '[1,2,3]'::vector FROM t");
9766 let Statement::Select(s) = s else { panic!() };
9767 let SelectItem::Expr { expr, .. } = &s.items[0] else {
9768 panic!()
9769 };
9770 assert!(matches!(
9771 expr,
9772 Expr::Cast {
9773 target: CastTarget::Vector,
9774 ..
9775 }
9776 ));
9777 }
9778
9779 #[test]
9780 fn unsupported_cast_target_errors() {
9781 let err = parse_statement("SELECT 1::numeric FROM t").unwrap_err();
9783 assert!(err.message.contains("unsupported cast target"));
9784 }
9785
9786 #[test]
9787 fn tx_statements_round_trip() {
9788 for q in ["BEGIN", "COMMIT", "ROLLBACK"] {
9789 let original = parse(q);
9790 let again = parse_statement(&original.to_string()).unwrap();
9791 assert_eq!(original, again);
9792 }
9793 }
9794
9795 #[test]
9796 fn interval_text_parsing_units() {
9797 assert_eq!(parse_interval_text("1 day"), Some((0, 86_400_000_000)));
9799 assert_eq!(parse_interval_text("1 second"), Some((0, 1_000_000)));
9800 assert_eq!(parse_interval_text("1 month"), Some((1, 0)));
9801 assert_eq!(parse_interval_text("2 years"), Some((24, 0)));
9802 assert_eq!(parse_interval_text("1 year 6 months"), Some((18, 0)));
9804 assert_eq!(
9805 parse_interval_text("1 day 2 hours"),
9806 Some((0, 86_400_000_000 + 7_200_000_000))
9807 );
9808 assert_eq!(parse_interval_text("-1 day"), Some((0, -86_400_000_000)));
9810 assert_eq!(parse_interval_text(""), None);
9812 assert_eq!(parse_interval_text("garbage"), None);
9813 assert_eq!(parse_interval_text("1 fortnight"), None);
9814 assert_eq!(parse_interval_text("1"), None);
9815 }
9816
9817 #[test]
9818 fn interval_literal_roundtrips_via_display() {
9819 let parsed = parse("SELECT INTERVAL '1 day 2 hours'");
9820 let s = parsed.to_string();
9821 assert!(s.contains("INTERVAL '1 day 2 hours'"), "got: {s}");
9823 let again = parse_statement(&s).unwrap();
9825 assert_eq!(parsed, again);
9826 }
9827
9828 #[test]
9831 fn parser_recognises_create_publication_bare() {
9832 let s = parse("CREATE PUBLICATION pub_a");
9833 let Statement::CreatePublication(p) = s else {
9834 panic!("expected CreatePublication, got {s:?}")
9835 };
9836 assert_eq!(p.name, "pub_a");
9837 assert_eq!(p.scope, PublicationScope::AllTables);
9838 }
9839
9840 #[test]
9841 fn parser_recognises_create_publication_for_all_tables() {
9842 let s = parse("CREATE PUBLICATION pub_a FOR ALL TABLES");
9843 let Statement::CreatePublication(p) = s else {
9844 panic!("expected CreatePublication, got {s:?}")
9845 };
9846 assert_eq!(p.name, "pub_a");
9847 assert_eq!(p.scope, PublicationScope::AllTables);
9848 }
9849
9850 #[test]
9851 fn parser_recognises_drop_publication() {
9852 let s = parse("DROP PUBLICATION pub_a");
9853 let Statement::DropPublication(name) = s else {
9854 panic!("expected DropPublication, got {s:?}")
9855 };
9856 assert_eq!(name, "pub_a");
9857 }
9858
9859 #[test]
9860 fn parser_recognises_for_table_list() {
9861 let s = parse("CREATE PUBLICATION pub_a FOR TABLE t1, t2, t3");
9862 let Statement::CreatePublication(p) = s else {
9863 panic!("expected CreatePublication, got {s:?}")
9864 };
9865 assert_eq!(p.name, "pub_a");
9866 let PublicationScope::ForTables(ts) = p.scope else {
9867 panic!("expected ForTables scope")
9868 };
9869 assert_eq!(ts, alloc::vec!["t1", "t2", "t3"]);
9870 }
9871
9872 #[test]
9873 fn parser_recognises_for_tables_plural() {
9874 let s = parse("CREATE PUBLICATION pub_a FOR TABLES t1, t2");
9876 let Statement::CreatePublication(p) = s else {
9877 panic!("expected CreatePublication, got {s:?}")
9878 };
9879 let PublicationScope::ForTables(ts) = p.scope else {
9880 panic!("expected ForTables")
9881 };
9882 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9883 }
9884
9885 #[test]
9886 fn parser_recognises_for_all_tables_except_list() {
9887 let s = parse("CREATE PUBLICATION p FOR ALL TABLES EXCEPT t1, t2");
9888 let Statement::CreatePublication(p) = s else {
9889 panic!()
9890 };
9891 let PublicationScope::AllTablesExcept(ts) = p.scope else {
9892 panic!("expected AllTablesExcept")
9893 };
9894 assert_eq!(ts, alloc::vec!["t1", "t2"]);
9895 }
9896
9897 #[test]
9898 fn parser_rejects_for_table_with_empty_list() {
9899 let err = parse_statement("CREATE PUBLICATION p FOR TABLE")
9901 .expect_err("must error on empty list");
9902 assert!(!err.message.is_empty());
9905 }
9906
9907 #[test]
9908 fn parser_recognises_show_publications() {
9909 let s = parse("SHOW PUBLICATIONS");
9912 assert!(matches!(s, Statement::ShowPublications));
9913 }
9914
9915 #[test]
9918 fn parser_recognises_create_subscription_single_publication() {
9919 let s = parse(
9920 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=127.0.0.1 port=20002' PUBLICATION pub_a",
9921 );
9922 let Statement::CreateSubscription(c) = s else {
9923 panic!("expected CreateSubscription, got {s:?}")
9924 };
9925 assert_eq!(c.name, "sub_a");
9926 assert_eq!(c.conn_str, "host=127.0.0.1 port=20002");
9927 assert_eq!(c.publications, alloc::vec!["pub_a"]);
9928 }
9929
9930 #[test]
9931 fn parser_recognises_create_subscription_multi_publication() {
9932 let s = parse("CREATE SUBSCRIPTION sub_a CONNECTION 'host=h' PUBLICATION p1, p2, p3");
9933 let Statement::CreateSubscription(c) = s else {
9934 panic!()
9935 };
9936 assert_eq!(c.publications, alloc::vec!["p1", "p2", "p3"]);
9937 }
9938
9939 #[test]
9940 fn parser_rejects_create_subscription_missing_connection() {
9941 let err = parse_statement("CREATE SUBSCRIPTION s PUBLICATION p")
9942 .expect_err("must error on missing CONNECTION");
9943 assert!(err.message.contains("CONNECTION"), "got: {}", err.message);
9944 }
9945
9946 #[test]
9947 fn parser_rejects_create_subscription_missing_publication() {
9948 let err = parse_statement("CREATE SUBSCRIPTION s CONNECTION 'host=x'")
9949 .expect_err("must error on missing PUBLICATION");
9950 assert!(err.message.contains("PUBLICATION"), "got: {}", err.message);
9951 }
9952
9953 #[test]
9954 fn parser_recognises_drop_subscription() {
9955 let s = parse("DROP SUBSCRIPTION sub_a");
9956 let Statement::DropSubscription(name) = s else {
9957 panic!("expected DropSubscription, got {s:?}")
9958 };
9959 assert_eq!(name, "sub_a");
9960 }
9961
9962 #[test]
9963 fn parser_recognises_show_subscriptions() {
9964 let s = parse("SHOW SUBSCRIPTIONS");
9965 assert!(matches!(s, Statement::ShowSubscriptions));
9966 }
9967
9968 #[test]
9969 fn parser_recognises_wait_for_wal_position_no_timeout() {
9970 let s = parse("WAIT FOR WAL POSITION 12345");
9971 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9972 panic!("expected WaitForWalPosition, got {s:?}")
9973 };
9974 assert_eq!(pos, 12345);
9975 assert!(timeout_ms.is_none());
9976 }
9977
9978 #[test]
9979 fn parser_recognises_wait_for_wal_position_with_timeout() {
9980 let s = parse("WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000");
9981 let Statement::WaitForWalPosition { pos, timeout_ms } = s else {
9982 panic!()
9983 };
9984 assert_eq!(pos, 67890);
9985 assert_eq!(timeout_ms, Some(5000));
9986 }
9987
9988 #[test]
9989 fn parser_rejects_wait_with_negative_position() {
9990 let err = parse_statement("WAIT FOR WAL POSITION -1").unwrap_err();
9996 assert!(!err.message.is_empty());
9997 }
9998
9999 #[test]
10000 fn parser_recognises_bare_analyze() {
10001 let s = parse("ANALYZE");
10002 assert!(matches!(s, Statement::Analyze(None)));
10003 }
10004
10005 #[test]
10006 fn parser_recognises_analyze_with_table() {
10007 let s = parse("ANALYZE users");
10008 let Statement::Analyze(Some(name)) = s else {
10009 panic!("expected Analyze, got {s:?}")
10010 };
10011 assert_eq!(name, "users");
10012 }
10013
10014 #[test]
10015 fn parser_recognises_analyze_with_quoted_table() {
10016 let s = parse("ANALYZE \"Mixed Case\"");
10017 let Statement::Analyze(Some(name)) = s else {
10018 panic!()
10019 };
10020 assert_eq!(name, "Mixed Case");
10021 }
10022
10023 #[test]
10024 fn parser_rejects_analyze_with_garbage_token() {
10025 let err = parse_statement("ANALYZE 42").expect_err("must error");
10026 assert!(!err.message.is_empty());
10027 }
10028
10029 #[test]
10030 fn analyze_display_roundtrips() {
10031 for sql in ["ANALYZE", "ANALYZE users"] {
10032 let s = parse(sql);
10033 let printed = s.to_string();
10034 let again = parse_statement(&printed)
10035 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10036 assert_eq!(s, again);
10037 }
10038 }
10039
10040 #[test]
10041 fn wait_for_display_roundtrips() {
10042 for sql in [
10043 "WAIT FOR WAL POSITION 12345",
10044 "WAIT FOR WAL POSITION 67890 WITH TIMEOUT 5000",
10045 ] {
10046 let s = parse(sql);
10047 let printed = s.to_string();
10048 let again = parse_statement(&printed)
10049 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10050 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10051 }
10052 }
10053
10054 #[test]
10055 fn subscription_ddl_display_roundtrips() {
10056 for sql in [
10057 "CREATE SUBSCRIPTION sub_a CONNECTION 'host=h port=20002' PUBLICATION pub_a",
10058 "CREATE SUBSCRIPTION sub_b CONNECTION 'host=h' PUBLICATION p1, p2",
10059 "DROP SUBSCRIPTION sub_a",
10060 "SHOW SUBSCRIPTIONS",
10061 ] {
10062 let s = parse(sql);
10063 let printed = s.to_string();
10064 let again = parse_statement(&printed)
10065 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10066 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10067 }
10068 }
10069
10070 #[test]
10071 fn parser_drop_dispatches_user_vs_publication() {
10072 let s = parse("DROP USER 'alice'");
10075 let Statement::DropUser(name) = s else {
10076 panic!("expected DropUser, got {s:?}")
10077 };
10078 assert_eq!(name, "alice");
10079 let s = parse("DROP PUBLICATION p1");
10081 assert!(matches!(s, Statement::DropPublication(_)));
10082 }
10083
10084 #[test]
10085 fn publication_ddl_display_roundtrips() {
10086 for sql in [
10089 "CREATE PUBLICATION pub_a",
10090 "CREATE PUBLICATION pub_a FOR ALL TABLES",
10091 "CREATE PUBLICATION pub_a FOR TABLE t1, t2",
10092 "CREATE PUBLICATION pub_a FOR ALL TABLES EXCEPT t1",
10093 "DROP PUBLICATION pub_a",
10094 "SHOW PUBLICATIONS",
10095 ] {
10096 let s = parse(sql);
10097 let printed = s.to_string();
10098 let again = parse_statement(&printed)
10099 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10100 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10101 }
10102 }
10103
10104 #[test]
10107 fn create_function_returns_trigger_plpgsql_minimal() {
10108 let sql = "CREATE FUNCTION noop() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN RETURN NEW; END; $$";
10109 let s = parse(sql);
10110 let Statement::CreateFunction(f) = s else {
10111 panic!("expected CreateFunction");
10112 };
10113 assert_eq!(f.name, "noop");
10114 assert!(!f.or_replace);
10115 assert!(f.args.is_empty());
10116 assert!(matches!(f.returns, FunctionReturn::Trigger));
10117 assert_eq!(f.language, "plpgsql");
10118 let FunctionBody::PlPgSql(block) = f.body else {
10119 panic!("expected PlPgSql body");
10120 };
10121 assert_eq!(block.statements.len(), 1);
10122 assert!(matches!(
10123 block.statements[0],
10124 PlPgSqlStmt::Return(ReturnTarget::New)
10125 ));
10126 }
10127
10128 #[test]
10129 fn create_function_or_replace_with_assignment() {
10130 let sql = "CREATE OR REPLACE FUNCTION update_sv() RETURNS TRIGGER LANGUAGE plpgsql AS $$
10133BEGIN
10134 NEW.search_vector := to_tsvector('english', NEW.subject);
10135 RETURN NEW;
10136END;
10137$$";
10138 let s = parse(sql);
10139 let Statement::CreateFunction(f) = s else {
10140 panic!("expected CreateFunction");
10141 };
10142 assert!(f.or_replace);
10143 let FunctionBody::PlPgSql(block) = &f.body else {
10144 panic!("expected PlPgSql body");
10145 };
10146 assert_eq!(block.statements.len(), 2);
10147 let PlPgSqlStmt::Assign { target, .. } = &block.statements[0] else {
10149 panic!("expected Assign as first stmt");
10150 };
10151 match target {
10152 AssignTarget::NewColumn(c) => assert_eq!(c, "search_vector"),
10153 other => panic!("expected NEW.col, got {other:?}"),
10154 }
10155 assert!(matches!(
10157 block.statements[1],
10158 PlPgSqlStmt::Return(ReturnTarget::New)
10159 ));
10160 }
10161
10162 #[test]
10163 fn create_trigger_after_insert_or_update() {
10164 let sql = "CREATE TRIGGER tg AFTER INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE FUNCTION update_sv()";
10165 let s = parse(sql);
10166 let Statement::CreateTrigger(t) = s else {
10167 panic!("expected CreateTrigger");
10168 };
10169 assert_eq!(t.name, "tg");
10170 assert_eq!(t.table, "messages");
10171 assert_eq!(t.timing, TriggerTiming::After);
10172 assert_eq!(t.events, vec![TriggerEvent::Insert, TriggerEvent::Update]);
10173 assert_eq!(t.for_each, TriggerForEach::Row);
10174 assert_eq!(t.function, "update_sv");
10175 }
10176
10177 #[test]
10178 fn create_trigger_before_delete_execute_procedure_alias() {
10179 let sql =
10181 "CREATE TRIGGER guard BEFORE DELETE ON t FOR EACH ROW EXECUTE PROCEDURE block_delete()";
10182 let s = parse(sql);
10183 let Statement::CreateTrigger(t) = s else {
10184 panic!("expected CreateTrigger");
10185 };
10186 assert_eq!(t.timing, TriggerTiming::Before);
10187 assert_eq!(t.events, vec![TriggerEvent::Delete]);
10188 }
10189
10190 #[test]
10191 fn drop_trigger_if_exists_round_trips() {
10192 let s = Statement::DropTrigger {
10197 name: "tg".into(),
10198 table: "messages".into(),
10199 if_exists: true,
10200 };
10201 assert_eq!(s.to_string(), "DROP TRIGGER IF EXISTS tg ON messages");
10202 }
10203
10204 #[test]
10205 fn trigger_ddl_display_roundtrips_through_parser() {
10206 for sql in [
10210 "CREATE TRIGGER tg AFTER INSERT ON t FOR EACH ROW EXECUTE FUNCTION f()",
10211 "CREATE TRIGGER tg2 BEFORE UPDATE OR DELETE ON t FOR EACH ROW EXECUTE FUNCTION g()",
10212 ] {
10213 let s = parse(sql);
10214 let printed = s.to_string();
10215 let again = parse_statement(&printed)
10216 .unwrap_or_else(|e| panic!("re-parse failed for {printed:?}: {e}"));
10217 assert_eq!(s, again, "round-trip mismatch for {sql:?}");
10218 }
10219 }
10220}