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